何时使用运算符<<指插入运算符,何时指按位左移

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

本文关键字:运算符 左移 何时指 何时使 插入      更新时间:2023-10-16

operator <<何时引用插入运算符,何时引用按位左移?

这将输出10operator <<指的是左移。

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;
}

这将输出 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.ba.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 == shiftstream << any == insertion。操作员的这种"重用"可能是令人困惑的,不死的。但是您可以通过使用括号来解决歧义:stream << (int << int) == "int"