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