operator <<何时指向插入运算符以及何时指向左移位?

jmm*_*mom 23 c++ operator-overloading associativity

什么时候operator <<引用插入操作符,什么时候引用按位左移?

这将输出10,并operator <<指向左移.

cout << a.b() << a.a.b << endl;  
Run Code Online (Sandbox Code Playgroud)

这将输出11,operator <<指的是插入运算符.

cout << a.b();
cout << a.a.b ;
Run Code Online (Sandbox Code Playgroud)

我很困惑,什么时候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;
}
Run Code Online (Sandbox Code Playgroud)

mar*_*inj 40

这将输出10,运算符<<参考左移.

cout << ab()<< aab << endl;

这是因为未指定操作数的评估顺序.使用clang它输出11但是使用gcc输出10.

你的代码:

cout << a.b() << a.a.b << endl;
Run Code Online (Sandbox Code Playgroud)

可以替换为:

std::cout.operator<<(a.b()).operator<<(a.a.b);  
Run Code Online (Sandbox Code Playgroud)

clang首先评估,a.b()然后a.a.bg ++以相反的方式做到了.由于您a.b()修改了变量,因此会得到不同的结果.

当您将代码重写为:

cout << a.b();
cout << a.a.b ;
Run Code Online (Sandbox Code Playgroud)

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

  • 同意.OP严格使用"1"和"0"的选择是不幸的,因为它掩盖了eval-order底层问题. (3认同)

Ant*_*vin 16

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

输出的差异是由函数参数的评估顺序引起的:

cout << a.b() << a.a.b
Run Code Online (Sandbox Code Playgroud)

operator<<(operator<<(cout, a.b()), a.a.b)
Run Code Online (Sandbox Code Playgroud)

所以输出取决于首先评估a.a.ba.b()评估.这实际上没有通过当前标准(C++ 14)指定,所以你也可以得到11.

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

更新:这似乎不正确,因为委员会决定(从N4606开始)采用P0145R2底部提到的不确定顺序参数评估.见[expr.call]/5.

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

但是,操作数按照内置操作符规定的顺序排序.

事实上,评估的顺序将在C++ 17中得到很好的定义.这个误解显然是由P0145的作者预测的:

我们并不认为这种不确定性会带来任何实质性的额外优化效益,但它确实会延续函数调用中评估顺序的混乱和危害.

  • @NathanOliver不,安东错了.改变是'aab`不会与`operator <<(cout,ab())`_interleaved_.因此`operator <<(cout,ab())`将在`aab`之前完全评估,反之亦然.因此,例如,评估`cout`然后`aab`然后`ab()`的顺序将不再符合. (3认同)
  • 更改(其中之一)是operator <<成为序列点,即使在重载时也是如此 (2认同)

ral*_*lfg 14

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

但是,您在命令行中遇到有关评估顺序的问题

cout << a.b() << a.a.b << endl;
Run Code Online (Sandbox Code Playgroud)

该功能a.b()有副作用.它交换值aaa和aab因此,很明显,在评估值ov之前或之后调用ab()a.a.b.

在C++中,未指定评估顺序,有关更详细的讨论,请参阅cppreference.com.


was*_*ful 9

这个电话:

cout << a.b() << a.a.b << endl;
Run Code Online (Sandbox Code Playgroud)

首先会考虑:

cout << a.b()
Run Code Online (Sandbox Code Playgroud)

它对应于插入操作符并返回cout的refence.因此,指令将变为:

(returned reference to cout) << a.a.b
Run Code Online (Sandbox Code Playgroud)

它将再次调用插入操作符等...

如果您的指示是:

cout << (a.b() << a.a.b) << endl;
Run Code Online (Sandbox Code Playgroud)

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

a.b() << a.a.b
Run Code Online (Sandbox Code Playgroud)

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


Mic*_*rus 9

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

((cout << a.b()) << a.a.b) << endl;
Run Code Online (Sandbox Code Playgroud)

或者以cout << a.b()当时<< a.a.b和之后的顺序排列<< endl.

此测序后,操作符重载发生在每次调用效果<<与给定的类型,然后确定哪一个过载被调用,因此,如果它是一个cout-operation或移位.