Search code examples
c++operatorsundefinedstandards-complianceoperator-precedence

Evaluation order of overloaded operator |?


5.15 Logical OR operator in the standard says the following:

Unlike |, || guarantees left-to-right evaluation;

Does this mean somewhere I cannot locate in the standard, | is defined to evaluate right-to-left, or that it is implementation-defined? Does this vary when the operator is overloaded? I wrote a quick program to test this and both MSVC++ and GCC seem to evaluate right-to-left.

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

This outputs 16. If ++a and --b are switched it outputs 19.

I've also considered that I may be running into the multiple changes between sequence points rule (and thus undefined behavior), but I'm unsure how/if that applies with two separate instances as operands.


Solution

  • As others said, it means that the order of the evaluation of the two sides is unspecified. To answer your other questions -

    I've also considered that I may be running into the multiple changes between sequence points rule (and thus undefined behavior)

    No, your case does not modify foo in between two adjacent sequence points. Before entering a function and before leaving a function, there always is a sequence point, which means that both modifications of foo happen in between two different pairs of sequence points.

    Does this vary when the operator is overloaded?

    All of clause 5 only talks about builtin operators. For user defined operator implementations, the rules don't apply. So also for ||, for user defined operators the order is not specified. But notice that it is only for user defined operators; not when both operands are converted to bool and trigger the builtin operator:

    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()) ...;
    }
    

    This will always first execute A::operator bool, and then B::operator bool. And it will only call p->dosomething() if p evaluates to true.