我今天发现了以下有趣的代码:
SomeFunction(some_bool_variable ? 12.f, 50.f : 50.f, 12.f)
我创建了一个小样本来重现行为:
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
。在这个表达式中,逗号左边的值被删除/忽略,所以我希望最终结果是:
SetSize(50.f);
混淆的第二部分是我们是否在三元组中使用true
或false
,将相同的2个值传递给函数。 true
案例应为h=12, w=50
我认为......
我看到编译器试图警告我某些事情,但我不太明白发生了什么。有人可以分解这个逻辑并逐步解释结果吗?
答案 0 :(得分:30)
虽然三元运算符的第二部分是自包含的,但第三部分却不是。语法如下:
条件表达式:
逻辑或表达
logical-or-expression?表达式:赋值表达式
所以你的函数调用实际上是这样的:
SetSize((true ? (12.f, 50.f): 50.f), 12.f)
因此三元表达式true ? (12.f, 50.f): 50.f
被评估为函数的第一个参数。然后12.f
作为第二个值传递。在这种情况下,逗号是不是逗号运算符,而是函数参数分隔符。
来自C++ standard的第5.18节:
2 在逗号被赋予特殊含义的上下文中,[ 示例:在函数参数列表(5.2.2)和初始值列表(8.5) - 结束示例]中描述的逗号运算符 在第5条中只能出现在括号中。 [ 示例:的
f(a, (t=3, t+2), c);
有三个参数,第二个参数的值为5。 - 结束 例子]
如果要将最后两个子表达式组合在一起,则需要添加括号:
SetSize(true ? 12.f, 50.f : (50.f, 12.f));
SetSize(false ? 12.f, 50.f : (50.f, 12.f));
现在你有一个逗号运算符,SetSize
的单个参数版本被调用。
答案 1 :(得分:17)
这是因为C++ does not treat the second comma as a comma operator:
各种以逗号分隔的列表中的逗号,例如函数参数列表
f(a, b, c)
和初始化列表int a[] = {1,2,3}
,不是逗号运算符。
就第一个逗号而言,C ++没有其他选择,只能将其视为逗号运算符。否则,解析将无效。
查看它的一种简单方法是认为只要C ++解析器在允许逗号分隔符的上下文中找到?
,它就会查找匹配的:
来完成表达式的第一部分,然后匹配尽可能少的完成第二个表达式。即使您删除了两个参数的重载,第二个逗号也不会被视为运算符。
答案 2 :(得分:10)
编译器警告你,你正在丢弃50%的浮点文字。
让我们分解它。
// void SetSize(float w, float h, float d=0)
SetSize(true ? 12.f, 50.f : 50.f, 12.f);
// ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^
这里我们提出一个表达式,使用条件运算符作为第一个参数,文字12.f
作为第二个参数;第三个参数保留默认值(0
)。
是的,真的。
它被解析为这样(因为没有其他有效的方法来解析它):
SetSize( (true ? 12.f, 50.f : 50.f), 12.f);
// ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^
第二个参数的值很简单,所以让我们检查第一个参数:
true ? 12.f, 50.f : 50.f
这意味着:
12.f, 50.f
50.f
嗯,真的总是如此,所以我们可以立即打折第二个选项。
表达式12.f, 50.f
使用逗号运算符,它会对两个操作数进行计算,然后将第一个操作数丢去,然后产生第二个操作数,即50.f
。
因此,整个事实上是:
SetSize(50.f, 12.f);
如果这不是一些神秘而毫无意义的编程“谜题”,那么这是一段非常愚蠢的代码,一位未受过教育的程序员希望将表达式“解包”成更相当于:
SetSize(
(true ? 12.f : 50.f),
(true ? 50.f : 12.f)
);
... 仍然是可怕且无用的代码,因为true仍然是真的。
(显然,在编写false
的情况下,值不同,但适用相同的逻辑。)
真实案例应为h = 12,w = 50我认为......
是的。这就是你发布的输出结果。当你不随意重新安排论证时,它会更清楚,即它们是w = 50 h = 12。