operator<< 什么时候指的是插入运算符,什么时候指的是按位左移?

When does operator<< refer to the insertion operator and when to the bitwise left shift?

什么时候operator <<指的是插入运算符,什么时候指的是按位左移?

这样会输出10,而operator <<指的是左移。

cout << a.b() << a.a.b << endl;  

这样会输出11operator <<指的是插入运算符。

cout << a.b();
cout << a.a.b ;

我很困惑,什么时候 operator <<(与 cout 一起使用时)指的是左移运算符?

#include <iostream> 
using namespace std; 

class A { 
public:
    A() { a.a = a.b = 1; }

    struct { int a, b; } a;

    int b(); 
}; 

int A::b(){
    int x=a.a;
    a.a=a.b;
    a.b=x; 
    return x;
};

 int main(){
    A a; 
    a.a.a = 0; 
    a.b(); 

    cout << a.b() << a.a.b << endl;      // ?????
    return 0;
}

没有括号,<<两边的操作数决定意义:int << int == shiftstream << any == insertion。 这个'reuse'的运算符可能会让人迷惑,死机。但是您可以使用括号解决歧义:stream << (int << int) == "int"

这次通话:

cout << a.b() << a.a.b << endl;

首先会考虑:

cout << a.b()

对应于插入运算符,returns 是对 cout 的引用。因此,指令将变为:

(returned reference to cout) << a.a.b

这将再次调用插入运算符等等...

如果您的指示是:

cout << (a.b() << a.a.b) << endl;

首先考虑括号之间的部分:

a.b() << a.a.b

这一次,你有一个介于 2 int 之间的运算符:编译器只能将其解析为对按位运算符的调用。

二元运算符,例如 <<,有两个定义其用法的属性:(运算符)优先级和(左或右)结合性。在这种情况下,关联性是关键,并且参见例如http://en.cppreference.com/w/c/language/operator_precedence<< 运算符具有从左到右的结合性,因此它们从左到右排序(就像用括号一样):

((cout << a.b()) << a.a.b) << endl;

或按 cout << a.b() 然后 << a.a.b 然后 << endl.

排序的单词

在此排序之后,运算符重载会在每次使用给定类型调用 << 时生效,然后确定调用哪个重载,从而确定它是 cout 操作还是移位。

This will output 10, and operator<< refer to left shift.

cout << a.b() << a.a.b << endl;

这是由于未指定操作数的计算顺序造成的。使用 clang 它输出 11 但使用 gcc 它输出 10.

您的代码:

cout << a.b() << a.a.b << endl;

可以替换为:

std::cout.operator<<(a.b()).operator<<(a.a.b);  

clang 首先计算 a.b() 然后 a.a.b,g++ 则相反。由于您的 a.b() 修改了变量,因此您会得到不同的结果。

当您将代码重写为:

cout << a.b();
cout << a.a.b ;

那么你有两个完整的表达式语句,这里没有与操作数评估相关的未指定行为。所以你总是得到相同的结果。

在您的例子中,所有 operator << 都是输出流插入运算符,因为它们的左参数是 ostream& 类型,并且它们从左到右分组。

输出的不同是由函数参数的求值顺序造成的:

cout << a.b() << a.a.b

operator<<(operator<<(cout, a.b()), a.a.b)

所以输出取决于 a.a.ba.b() 中的哪一个先被评估。当前标准 (C++14) 实际上并未指定,因此您也可以获得 11

C++17 中的 AFAIK 11 将是这两种情况的唯一有效输出,因为它强制对函数参数进行从左到右的评估。

更新:这似乎不是真的,因为委员会决定(截至 N4606) to go with indeterminately sequenced parameter evaluation mentioned at the bottom of P0145R2。见 [expr.call]/5.

更新 2:由于我们在这里讨论的是重载运算符,因此适用 N4606 中的 [over.match.oper]/2,即

However, the operands are sequenced in the order prescribed for the built-in operator.

所以确实,计算的顺序将在 C++17 中得到明确定义。这种误解显然已经被P0145的作者预测到了:

We do not believe that such a nondeterminism brings any substantial added optimization benefit, but it does perpetuate the confusion and hazards around order of evaluations in function calls

您遇到的问题与 << 运算符无关。在每种情况下,都会调用插入运算符。

但是,您在命令行

中遇到了有关 求值顺序 的问题
cout << a.b() << a.a.b << endl;

函数a.b()有副作用。它交换值 a.a.a 和 a.a.b。因此,很明显,a.b() 是在评估值 ov a.a.b.

之前还是之后被调用的

在 C++ 中,未指定求值顺序,请参阅 cppreference.com 了解更详细的讨论。