在三元expression中与逗号混淆

今天我发现了以下有趣的代码:

SomeFunction(some_bool_variable ? 12.f, 50.f : 50.f, 12.f) 

我创build了一个小样本来重现行为:

 class Vector3f { public: Vector3f(float val) { std::cout << "vector constructor: " << val << '\n'; } }; void SetSize(Vector3f v) { std::cout << "SetSize single param\n"; } void SetSize(float w, float h, float d=0) { std::cout << "SetSize multi param: " << w << ", " << h << ", " << d << '\n'; } int main() { SetSize(true ? 12.f, 50.f : 50.f, 12.f); SetSize(false ? 12.f, 50.f : 50.f, 12.f); } 

( 现场示例 )

我运行上面的代码得到的结果是:

 clang++ -std=c++14 -O2 -Wall -pedantic -lboost_system -lboost_filesystem -pthread main.cpp && ./a.out main.cpp:29:20: warning: expression result unused [-Wunused-value] SetSize(true ? 12.f, 50.f : 50.f, 12.f); ^~~~ main.cpp:30:21: warning: expression result unused [-Wunused-value] SetSize(false ? 12.f, 50.f : 50.f, 12.f); ^~~~ 2 warnings generated. SetSize multi param: 50, 12, 0 SetSize multi param: 50, 12, 0 

我期望在这两种情况下,一个参数将被传递给SetSize(float) 。 然而,两个参数被传递,我觉得非常困惑(特别是因为三元优先于逗号;所以我认为逗号不是在这种情况下定界函数的参数)。 例如,如果使用true ,那么三元应该导致12.f, 50.f 。 在这个expression式中,逗号左边的值被丢弃/忽略,所以我希望最终的结果是:

 SetSize(50.f); 

第二部分的困惑是,无论我们在三元组中使用true还是false ,相同的2个值被传递给函数。 true情况应该是h=12, w=50我想…

我看到编译器试图提醒我一些事情,但是我不太明白发生了什么。 有人可以分解这个逻辑,并一步一步地解释结果吗?

三元运算符的第二部分是自包含的,而第三部分则不是。 语法如下:

条件expression式

逻辑或expression

逻辑或expression式? expression式:赋值expression式

所以你的函数调用是这样的:

 SetSize((true ? (12.f, 50.f): 50.f), 12.f) 

那三元的expression是true ? (12.f, 50.f): 50.f true ? (12.f, 50.f): 50.f被评估为函数的第一个参数。 然后12.f作为第二个值传递。 这种情况下的逗号不是逗号运算符,而是函数参数分隔符。

从C ++标准的第5.18节:

2在逗号赋予特殊意义的上下文中,[ 例子:在函数参数列表(5.2.2)和初始值设定项列表(8.5) – 例子结尾 ]中,第5章中描述的逗号运算符只能出现在括号内。 [ 例如:

 f(a, (t=3, t+2), c); 

有三个论点,其中第二个是价值5。 – 结束示例 ]

如果您希望将最后两个子expression式分组在一起,则需要添加括号:

 SetSize(true ? 12.f, 50.f : (50.f, 12.f)); SetSize(false ? 12.f, 50.f : (50.f, 12.f)); 

现在你有一个逗号运算符, SetSize的单参数版本被调用。

这是因为C ++不把第二个逗号当作逗号操作符 :

逗号分隔的列表中的逗号(例如函数参数列表f(a, b, c)和初始值设定项列表int a[] = {1,2,3} )不是逗号运算符。

就第一个逗号而言,C ++没有别的select,只能把它当作一个逗号操作符。 否则,parsing将是无效的。

查看它的一个简单的方法是认为只要C ++分析器发现? 在允许逗号分隔符的上下文中,它会查找匹配项:完成expression式的第一部分,然后尽可能less地匹配以完成第二个expression式。 即使删除了两个参数的重载,第二个逗号也不会被视为操作符。

编译器警告你,你正在扔掉你的浮点文字的50%。

让我们分解它。

 // void SetSize(float w, float h, float d=0) SetSize(true ? 12.f, 50.f : 50.f, 12.f); // ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ 

在这里,我们提出一个使用条件运算符作为第一个参数,文字12.f作为第二个参数的expression式; 第三个参数保留默认值( 0 )。

对真的。

它是这样parsing的(因为没有其他有效的parsing方法):

 SetSize( (true ? 12.f, 50.f : 50.f), 12.f); // ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ 

第二个参数的价值是直截了当的,所以让我们来看看第一个:

 true ? 12.f, 50.f : 50.f 

意即:

  • 如果是,则结果是12.f, 50.f
  • 否则,结果是50.f

那么,真的是永远是真的,所以我们可以立即打折第二个选项。

expression式12.f, 50.f使用了逗号运算符 ,它对两个操作数进行求值,然后将第一个操作数和第二个操作数进行比较,即50.f

因此,整个事情其实是:

 SetSize(50.f, 12.f); 

如果这不是一些神秘而毫无意义的编程“难题”,那么这是一段非常愚蠢的代码,一个没有受过教育的程序员希望将expression式“解压”成更加等价的东西:

 SetSize( (true ? 12.f : 50.f), (true ? 50.f : 12.f) ); 

…这仍然是可怕的和无用的代码,因为真实仍然是真实的。

(显然,在写入false的情况下,这些值是不同的,但应用相同的逻辑。)


真正的情况应该是h = 12,w = 50我想…

它是。 这就是你发布的输出说的。 当你不擅自重新安排参数时,它是更清楚的,即它们是w = 50 h = 12。