cout的正确答案是什么<< a ++<<一个;?

时间:2012-05-28 10:09:55

标签: c++ c++-faq

最近在一次采访中,有一个以下客观类型的问题。

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'。

4 个答案:

答案 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<<E2E1>>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<<;这会改变 aa++)的存储值,除了确定之外,还访问它 新值(第二个a),行为未定义。

答案 3 :(得分:4)

正确的答案是质疑这个问题。这种说法是不可接受的,因为读者看不清楚答案。另一种看待它的方法是我们引入了副作用(c ++),这使得语句更难以解释。简洁的代码很棒,只要它的含义很明确。