2017-05-31 59 views
34

逗号我发现下面的有趣的代码今天:混乱与三元表达

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); 
} 

Live Sample

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

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); 

混乱的第二部分是,我们是否使用truefalse三元,相同的2个值被传递给函数。 true的情况应该是h=12, w=50我想......

我看到编译器试图警告我一些事情,但我不明白是怎么回事。有人可以分解这种逻辑,并逐步解释结果吗?

+1

您不能使用三元运算符提供参数列表。它只选择值。 – EJP

+6

@EJP:让我们用回答部分来回答问题。谢谢。 –

+0

条件运算符的语言语法不允许在最后一部分使用逗号 - 运算符表达式(为了避免歧义) –

回答

30

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

条件表达式

逻辑或表达

逻辑或表达?表达式:赋值表达式

所以你的函数调用是有效的:

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

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

C++ standard的部分5.18:

在上下文其中逗号被赋予特殊的含义,[ 例:中的参数列表,以函数(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被调用。

17

这是因为C++ does not treat the second comma as a comma operator

在各种逗号分隔的列表,诸如函数的参数列表f(a, b, c)和初始化列表int a[] = {1,2,3}逗号,不是逗号运算符。

就第一个逗号而言,C++没有别的选择,只能把它当作逗号运算符。否则,解析将是无效的。

查看它是认为只要C++解析器在上下文中发现?其中逗号分隔符是允许的,它会寻找匹配:完成表达式的第一部分,和然后的一个简单的方法相匹配少是完成第二个表达式所必需的。即使删除了双重过载,第二个逗号也不会被视为运算符。

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 

这意思是:

  • 如果为true,则结果为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) 
); 

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

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


真正情况下应为h = 12,W = 50 I” d think ...

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