最近在一次采访中,有一个以下客观类型的问题。
int a = 0;
cout << a++ << a;
数目:
一个。 10个
湾01
C。未定义的行为
我回答了选择b,即输出为“01”。
但令我惊讶的是后来有一位采访者告诉我,正确答案是选项c:未定义。
现在,我确实知道C ++中序列点的概念。以下语句的行为未定义:
int i = 0;
i += i++ + i++;
但根据我对语句cout << a++ << a
的理解,ostream.operator<<()
将被调用两次,首先是ostream.operator<<(a++)
,后来是ostream.operator<<(a)
。
我还检查了VS2010编译器的结果,其输出也是'01'。
答案 0 :(得分:142)
你可以想到:
cout << a++ << a;
如:
std::operator<<(std::operator<<(std::cout, a++), a);
C ++保证先前评估的所有副作用都将在sequence points执行。函数参数评估之间没有序列点,这意味着可以在参数a
之前或之后评估参数std::operator<<(std::cout, a++)
。所以上面的结果是不确定的。
C ++ 17更新
在C ++ 17中,规则已经更新。特别是:
在移位运算符表达式
E1<<E2
和E1>>E2
中,E1
的每个值计算和副作用都会在E2
的每个值计算和副作用之前排序。< / p>
这意味着它需要代码生成结果b
,后者输出01
。
有关详细信息,请参阅P0145R3 Refining Expression Evaluation Order for Idiomatic C++。
答案 1 :(得分:68)
从技术上讲,总的来说,这是未定义的行为。
但是,答案有两个重要方面。
代码声明:
std::cout << a++ << a;
评估为:
std::operator<<(std::operator<<(std::cout, a++), a);
标准没有定义函数参数的评估顺序 所以要么:
std::operator<<(std::cout, a++)
或a
或根据标准,此订单为未指定 [Ref 1] 。
[参考1] C ++ 03 5.2.2函数调用
第8段
参数的评估顺序未指定。参数表达式求值的所有副作用在输入函数之前生效。后缀表达式和参数表达式列表的评估顺序未指定。
此外,在评估函数的参数之间没有序列点,但只有在评估了所有参数 [Ref 2] 之后才存在序列点。
[参考2] C ++ 03 1.9程序执行[intro.execution]:
第17段:
当调用函数时(无论函数是否为内联函数),在评估函数体中任何表达式或语句执行之前发生的所有函数参数(如果有)之后,都会有一个序列点。 p>
请注意,这里c
的值被多次访问而没有插入序列点,关于此标准说:
[参考3] C ++ 03 5表达式[expr]:
第4段:
....
在前一个和下一个序列点之间,标量对象的表达式评估最多只能修改一次存储值。此外,只能访问先前值以确定要存储的值。对于完整的子表达式的每个允许排序,应满足本段的要求 表达; 否则行为未定义。
代码不会在没有插入序列点的情况下多次修改c
,并且不会访问它来确定存储对象的值。这明显违反了上述条款,因此标准规定的结果是未定义行为 [Ref 3] 。
答案 2 :(得分:19)
序列点仅定义部分排序。在你的情况下,你有 (一旦完成重载决定):
std::cout.operator<<( a++ ).operator<<( a );
a++
和第一次通话之间有一个序列点
std::ostream::operator<<
,并且之间有一个序列点
第二次a
和第二次调用std::ostream::operator<<
,但在那里
a++
和a
之间没有序列点;唯一的订单
约束是a++
被充分评估(包括副作用)
在第一次调用operator<<
之前,第二次a
完全调用之前
在第二次调用operator<<
之前评估。 (还有
因果排序约束:第二次调用operator<<
不能
首先是第一个,因为它需要第一个结果作为
论证。)§5/ 4(C ++ 03)陈述:
除非另有说明,否则顺序为 评估个体经营者的操作数和子表达式 个别表达,以及副作用发生的顺序, 没有具体说明。在前一个和下一个序列之间点一个标量 对象的存储值最多只能修改一次 表达的评价。此外,先前的值应为 仅访问以确定要存储的值。的要求 对于每个允许的订购,应满足本款 表达式的子表达式;否则行为是 未定义。
您的表达式允许的排序之一是a++
,a
,首先
致电operator<<
,第二次致电operator<<
;这会改变
a
(a++
)的存储值,除了确定之外,还访问它
新值(第二个a
),行为未定义。
答案 3 :(得分:4)
正确的答案是质疑这个问题。这种说法是不可接受的,因为读者看不清楚答案。另一种看待它的方法是我们引入了副作用(c ++),这使得语句更难以解释。简洁的代码很棒,只要它的含义很明确。