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.b
g ++以相反的方式做到了.由于您a.b()
修改了变量,因此会得到不同的结果.
当您将代码重写为:
cout << a.b();
cout << a.a.b ;
Run Code Online (Sandbox Code Playgroud)
那么你有两个完整的表达式语句,这里没有与操作数评估相关的未指定行为.所以你总能获得相同的结果.
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.b
或a.b()
评估.这实际上没有通过当前标准(C++ 14)指定,所以你也可以得到11
.
C++ 17中的AFAIK 11
将是这两种情况的唯一有效输出,因为它强制执行从左到右的函数参数评估.
更新:这似乎不正确,因为委员会决定(从N4606开始)采用P0145R2底部提到的不确定顺序参数评估.见[expr.call]/5.
Update2:由于我们在这里讨论重载运算符,N4606中的[over.match.oper]/2适用,
但是,操作数按照内置操作符规定的顺序排序.
事实上,评估的顺序将在C++ 17中得到很好的定义.这个误解显然是由P0145的作者预测的:
我们并不认为这种不确定性会带来任何实质性的额外优化效益,但它确实会延续函数调用中评估顺序的混乱和危害.
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.
这个电话:
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
:编译器只能将其解析为对按位运算符的调用.
二元运算符(例如<<
)具有两个定义其用法的属性:(运算符)优先级和(左或右)关联性.在这种情况下,关联性是关键,并且,例如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或移位.