重载运算符的评估顺序|?

时间:2011-08-25 05:56:36

标签: c++ operators undefined standards-compliance operator-precedence

5.15标准中的逻辑OR运算符表示如下:

  

与|,||不同保证从左到右的评估;

这是否意味着我无法在标准中找到,|被定义为从右到左评估,或者它是实现定义的?当操作员超载时,这会有所不同吗?我写了一个快速程序来测试这个,MSVC ++和GCC似乎都在评估从右到左

#include<iostream>
using namespace std;

int foo = 7;

class Bar {
public:
    Bar& operator|(Bar& other) {
        return *this;
    }
    Bar& operator++() {
        foo += 2;
        return *this;
    }
    Bar& operator--() {
        foo *= 2;
        return *this;
    }
};

int main(int argc, char** argv) {
    Bar a;
    Bar b;
    Bar c = ++a | --b;
    cout << foo;
}

这会输出16。 如果切换++a--b,则会输出19

我还认为我可能会遇到序列点规则之间的多个变化(因此未定义的行为),但我不确定如何将两个单独的实例应用为操作数。

3 个答案:

答案 0 :(得分:4)

暂时忽略该操作符,并注意这一点:

(x + y) * (z + 1)

这里,必须在乘法发生之前评估两个操作数(否则我们不知道要乘以什么)。在C ++中,完成此操作的顺序是未指定:首先可以是(x + y),或者首先是(z + 1),无论编译器感觉更好。†

运营商|也是如此。但是,运算符||必须短路,为了做到这一点,它必须严格从左到右进行评估。 (如果左侧评估结果为true,则评估结束时不会评估正确的操作数。)这就是句子的含义。

†请注意,它可能没有这种或那种偏好,只是按照它列出的顺序进行评估。这就是你获得输出的原因,尽管你不能在语言层面上依赖它。

答案 1 :(得分:2)

  

这是否意味着我无法在标准中找到某个地方,|被定义为从右到左评估,还是由实现定义?

迂腐地说,|运算符的参数的评估顺序是未指定的。这意味着可以按任何顺序评估操作数。

然而,从左到右指定了逻辑运算符(即&&||等)和逗号运算符的操作数的评估顺序。

答案 2 :(得分:2)

正如其他人所说,这意味着双方的评估顺序没有具体说明。回答你的其他问题 -

  

我还认为我可能会遇到序列点规则之间的多重变化(因而未定义的行为)

不,您的案例不会修改两个相邻序列点之间的foo。在进入函数之前和离开函数之前,始终存在序列点,这意味着foo的两个修改都发生在两个不同的序列点对之间。

  

当操作员超载时,这会有所不同吗?

第5条的全部内容仅涉及内置运营商。对于用户定义的运算符实现,规则不适用。同样对于||,对于用户定义的运算符,未指定顺序。但请注意,它仅适用于用户定义的运算符;不是当两个操作数都转换为bool并触发内置运算符时:

struct A { 
  operator bool() const { return false; }
};

struct B {
  operator bool() const { return true; }
};

int main() {
  A a;
  B b;
  a || b;

  shared_ptr<myclass> p = ...;
  if(p && p->dosomething()) ...;
}

这将始终首先执行A::operator bool,然后B::operator bool。如果p->dosomething()评估为p,则只会致电true