cout << 函数调用顺序打印的顺序是什么?

29

以下代码:

myQueue.enqueue('a');
myQueue.enqueue('b');
cout << myQueue.dequeue() << myQueue.dequeue();

在控制台上打印“ba”

当:

myQueue.enqueue('a');
myQueue.enqueue('b');
cout << myQueue.dequeue();
cout << myQueue.dequeue();

为什么会打印出 "ab"?

似乎cout先调用最外层的函数(即最靠近分号的函数),然后逐步向内部工作,是这样的吗?


所有的答案都去哪了?现在只剩下一个了吗? - finiteloop
1
回答者们删掉了它们,因为他们意识到它们是错误的。 - anon
1
有些人在发现自己的答案错误时会将其删除。 - Anon.
3个回答

32
< p >由于使用< code ><<操作符时不存在顺序点,因此编译器可以自由地先计算dequeue函数中的任意一个。保证的是第二个dequeue调用(按照表达式中出现的顺序而不一定是它们被计算的顺序)的结果将与第一个<<操作的结果进行<<运算(如果你明白我的意思的话)。

因此,编译器可以将您的代码转换为以下任何一种(伪中间C ++代码)。这不是详尽列表。

auto tmp2 = myQueue.dequeue();
auto tmp1 = myQueue.dequeue();
std::ostream& tmp3 = cout << tmp1;
tmp3 << tmp2;

或者

auto tmp1 = myQueue.dequeue();
auto tmp2 = myQueue.dequeue();
std::ostream& tmp3 = cout << tmp1;
tmp3 << tmp2;
或者
auto tmp1 = myQueue.dequeue();
std::ostream& tmp3 = cout << tmp1;
auto tmp2 = myQueue.dequeue();
tmp3 << tmp2;

以下是临时变量在原始表达式中所对应的内容。

cout << myQueue.dequeue() << myQueue.dequeue();
|       |               |    |               |
|       |____ tmp1 _____|    |_____ tmp2 ____|
|                       |
|________ tmp3 _________|

所以,在上面的例子中,这段代码: std::ostream& tmp3 = cout << tmp1; tmp3 << tmp2; 相当于说“cout << tmp1 << tmp2;”吗?还是我漏掉了什么? - finiteloop
@segfault:是的,因为这是C++语法中<<的结合方式。a << b << c总是被分组为(a << b) << c - CB Bailey
但是按照这个逻辑,不是说 cout << a << b 就等同于 (cout << a) << b 并且会先执行 cout a 的操作(例如调用 myQueue.dequeue())吗? - finiteloop
6
结合运算不涉及子表达式计算的顺序,结合律无法说明子表达式计算的顺序。cout << a << b 总是表示为 (cout << a) << b ,但编译器可以自由地首先计算 b,然后计算 a,接着执行第一个 << 操作和第二个 << 操作。这正是我在答案中所指出的要点。 - CB Bailey
抱歉评论了一个旧帖子,但是C++11 1.9/15(C++03 1.9/17)似乎意味着重载运算符的顺序约束不同。如果我的理解是正确的,上述代码将是 cout.operator<<(a).operator<<(b)或者operator<<(operator<<(cout,a),b),这取决于运算符是成员函数还是自由函数。两者都会为子表达式 ab 意味着定义好的评估顺序。这样对吗? - jogojapan
@jogojapan:尽管C++11中的“序列点”概念已被替换,但这个特定事实并没有改变。在您展示的任何表达式中,都没有ab之间的评估约束。它们中的任何一个都可能在另一个之前进行评估。还有其他约束条件,例如b必须在两者的直接或间接参数中作为operator<<之前进行评估,而a必须在其为直接参数的operator<<之前进行评估,但是表达式coutab可以以任何顺序相对于彼此进行评估。 - CB Bailey

9

您的示例中的调用:

cout << myQueue.dequeue() << myQueue.dequeue();

使用两个 operator<< 函数调用,将其翻译成以下表达式:

operator<<( operator<<( cout, myQueue.dequeue() ), myQueue.dequeue() );
-------------------- 1
---------2

coutmyQueue.dequeue()的评估顺序是未指定的。但是,operator<<函数调用的顺序是明确定义的,如标有12


6
自C++17以来,此代码的行为已更改。即使它是一个重载运算符,<<的左操作数也会在<<的右操作数之前被排序。现在的输出必须是ab
有关更多信息,请参见:C++17引入了哪些评估顺序保证?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接