与三元表达中的逗号混淆

voi*_*ter 34 c++

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

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章中描述的逗号运算符只能出现在括号中.[ 例如:

f(a, (t=3, t+2), c);
Run Code Online (Sandbox Code Playgroud)

有三个参数,第二个参数的值为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++解析器?在允许逗号分隔符的上下文中找到它,它就会查找匹配:以完成表达式的第一部分,然后匹配尽可能少的完成第二部分表达.即使删除了两个参数的重载,第二个逗号也不会被视为运算符.


Lig*_*ica 9

编译器警告你,你正在丢弃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)

这意味着:

  • 如果为true,则结果为 12.f, 50.f
  • 否则,结果是 50.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.