operator <<
何时引用插入运算符,何时引用按位左移?
这将输出10
,operator <<
表示左移。
cout << a.b() << a.a.b << endl;
这将输出11
,operator <<
指的是插入运算符。
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;
}
答案 0 :(得分:40)
这将输出10,并且运算符&lt;&lt;参考左移。
cout&lt;&lt; a.b()&lt;&lt; a.a.b&lt;&lt; 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 ;
然后你有两个完整的表达式语句,这里没有与操作数评估相关的未指定行为。所以你总能获得相同的结果。
答案 1 :(得分:16)
在您的情况下,所有operator <<
都是输出流插入运算符,因为它们的左参数类型为ostream&
,并且它们从左到右分组。
输出的差异是由函数参数的评估顺序引起的:
cout << a.b() << a.a.b
是
operator<<(operator<<(cout, a.b()), a.a.b)
因此输出取决于首先评估a.a.b
或a.b()
中的哪一个。这实际上没有通过当前标准(C ++ 14)指定,因此您也可以获得11
。
C ++中的AFAIK 17 11
将是两种情况的唯一有效输出,因为它强制执行从左到右的函数参数评估。
更新:这似乎不正确,因为委员会决定(从N4606开始)采用P0145R2底部提到的不确定顺序参数评估。见[expr.call] / 5。
Update2:由于我们在这里谈论重载运算符,N4606中的[over.match.oper] / 2适用,其中说
但是,操作数按照内置运算符规定的顺序排序。
的确,评估的顺序将在C ++ 17中得到很好的定义。这种误解显然是由P0145的作者预测的:
我们并不认为这种不确定性会带来任何实质性的额外优化效益,但它会使函数调用中的评估顺序长期存在混淆和危害
答案 2 :(得分:14)
您遇到的问题与&lt;&lt;&lt;&lt;运营商。在每种情况下,都会调用插入运算符。
但是,您在命令行中遇到有关评估顺序的问题
cout << a.b() << a.a.b << endl;
函数a.b()
有副作用。它交换了值a.a.a和a.a.b.因此,很明显,在评估值a.a.b
之前或之后调用a.b()。
在C ++中,未指定评估顺序,有关更详细的讨论,请参阅cppreference.com。
答案 3 :(得分:9)
这个电话:
cout << a.b() << a.a.b << endl;
首先会考虑:
cout << a.b()
对应于插入运算符并将refence返回给cout。因此,指令将变为:
(returned reference to cout) << a.a.b
再次调用插入操作符等等......
如果您的指示是:</ p>
cout << (a.b() << a.a.b) << endl;
括号之间的部分将首先考虑:
a.b() << a.a.b
这次,你有一个2 int
之间的运算符:编译器只能将其解析为对按位运算符的调用。
答案 4 :(得分:9)
二进制运算符(例如<<
)具有两个属性,用于定义其用法:(运算符)优先级和(左或右)关联性。在这种情况下,关联性是关键,并参见例如http://en.cppreference.com/w/c/language/operator_precedence,<<
运算符具有从左到右的关联性,因此它们从左到右排序(就像用括号一样):
((cout << a.b()) << a.a.b) << endl;
或以cout << a.b()
然后<< a.a.b
然后<< endl
排序的字词。
在此排序之后,运算符重载将在每次调用具有给定类型的<<
时生效,然后确定调用哪个重载,从而确定它是cout
- 操作还是移位。
答案 5 :(得分:4)
没有括号,<<
两侧的操作数确定含义:int << int == shift
,stream << any == insertion
。
这个&#39;重用&#39;运营商可能会混淆,独立。但您可以使用括号来解决歧义:stream << (int << int) == "int"