何时使用运算符<<指插入运算符,何时指按位左移
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;
这将输出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;
}
这将输出 10,运算符<<引用左移。
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 ;
然后你有两个完整的表达式语句,这里没有与操作数计算相关的未指定行为。所以你总是得到相同的结果。
您遇到的问题与<<运算符无关。在每种情况下,都会调用插入运算符。
但是,您面临有关命令行中评估顺序的问题
cout << a.b() << a.a.b << endl;
函数a.b()
有一个副作用。它交换值 a.a.a 和 a.a.b。因此,很明显,a.b(( 是在评估值 ov a.a.b
之前还是之后调用的。
在C++中,未指定评估顺序,有关更详细的讨论,请参阅 cppreference.com。
在您的情况下,所有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++17 11
中的 AFAIK 将是这两种情况下唯一有效的输出,因为它强制执行函数参数的从左到右计算。
更新:这似乎不是真的,因为委员会决定(截至 N4606(采用P0145R2底部提到的不确定顺序参数评估。参见 [expr.call]/5。
更新2:由于我们在这里谈论的是重载运算符,因此 N4606 中的 [over.match.oper]/2 适用,它说
但是,操作数按为内置运算符规定的顺序进行排序。
因此,评估的顺序将在C++17中明确定义。P0145的作者显然已经预测到了这种误解:
我们不认为这种非确定性会带来任何实质性的附加优化优势,但它确实使函数调用中评估顺序的混乱和危险永久化。
此调用:
cout << a.b() << a.a.b << endl;
将首先考虑:
cout << a.b()
对应于插入运算符并返回对 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
操作还是移位。
没有括号,<<
两侧的操作数确定含义:int << int == shift
,stream << any == insertion
。操作员的这种"重用"可能是令人困惑的,不死的。但是您可以通过使用括号来解决歧义:stream << (int << int) == "int"
- 使用boost::multiprecision cpp_int左移时出现超时错误
- 警告:左移计数 >= 在 C++ 中将字节流读取为双精度变量时的类型宽度
- 左移<<如何在不同的功能中给出不同的结果?
- 使用 AVX2 指令左移 128 位数字
- <<(按位左移)在 Swift 中的数组上做什么?
- 带有左移操作员C 的意外输出
- 编译器将输出的流运算符<<解释为用于按位左移的二进制运算符<<
- 左移负整数为零是否有意义?
- 在 VS 移位中,UINT64 上的左移(64 >移位> 32)位仅 32 位
- C++20 是否为"overflow"的有符号整数很好地定义了左移?
- C++左移操作
- 为什么编译器左移 0
- 如何在位明智的操作中选择正确的左移
- 虽然左移运算符 (<<) 在 std::cout 之前使用,但它有什么用?
- 使用左移运算符将 F 转换为 FF
- 重载左移和右移运算符(cin和cout)
- 重载运算符'<<'(左移)的显式专用化
- 奇怪的积分提升用左移运算符
- 何时使用运算符<<指插入运算符,何时指按位左移
- 测试左移运算符是否存在