Порядок оценки перегруженного оператора |?

5.15 Логический оператор ИЛИ в стандарте говорит следующее:

В отличие от |, || гарантирует оценку слева направо;

Означает ли это, что где-то я не могу найти в стандарте, | определен для оценки справа налево или что он определяется реализацией? Изменяется ли это, когда оператор перегружен? Я написал быструю программу, чтобы проверить это, и 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.

Я также подумал, что могу столкнуться с множественными изменениями между правилом точек последовательности (и, следовательно, с неопределенным поведением), но я не уверен, как/если это применимо к двум отдельным экземплярам в качестве операндов.


person 0x5f3759df    schedule 25.08.2011    source источник
comment
Обратите внимание, что хотя a и b являются разными экземплярами, они оба относятся к одному и тому же foo, поэтому вы действительно вызываете неопределенное поведение, поскольку вы записываете одну и ту же переменную несколько раз без промежуточной точки последовательности.   -  person Matthieu M.    schedule 25.08.2011
comment
Вызов каждого оператора — это точка последовательности, так что все в порядке.   -  person Bo Persson    schedule 25.08.2011


Ответы (3)


Как говорили другие, это означает, что порядок оценки двух сторон не указан. Чтобы ответить на другие ваши вопросы -

Я также подумал, что могу столкнуться с несколькими изменениями между правилом точек последовательности (и, следовательно, с неопределенным поведением)

Нет, ваш случай не изменяет 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.

person Johannes Schaub - litb    schedule 25.08.2011
comment
Странный способ обработки || и && говорит о том, что их почти никогда не перегружают, потому что почти все такие случаи, которые я могу придумать, будут очень запутанными. О единственном случае, о котором я мог подумать, этого не было бы, если бы вы использовали их для совершенно чужой цели, такой как нотация С++ для грамматики BNF или что-то в этом роде. - person Omnifarious; 25.08.2011

Пока игнорируйте этого оператора и просто обратите внимание на это:

(x + y) * (z + 1)

Здесь оба операнда должны быть оценены, прежде чем умножение может иметь место (иначе мы не знали бы, что умножать). В C++ порядок, в котором это делается, не указан: это может быть (x + y) первым или (z + 1) первым, в зависимости от того, что компилятор считает лучшим.†

То же верно и для оператора |. Однако оператор || должен замыкаться, а для этого он должен выполнять вычисления строго слева направо. (И если левая оценка дает true, оценка заканчивается без оценки правого операнда.) Вот что означает это предложение.

† Обратите внимание, что у него может не быть предпочтений в том или ином случае, и просто оценивайте в том порядке, в котором он указан. Вот почему вы получаете то, что делаете, хотя и не можете полагаться на него на уровне языка.

person GManNickG    schedule 25.08.2011
comment
Разве это не должно заканчивать оценку, если левый операнд возвращает true? - person sharvey; 25.11.2011

Означает ли это, что я не могу найти в стандарте | определен для оценки справа налево или что он определяется реализацией?

Педантично говоря, порядок вычисления аргументов оператора | не указан. Это означает, что операнды могут быть оценены в любом порядке.

Однако указан порядок вычисления операндов логических операторов (т.е. &&, || и т.д.) и оператора запятой, т.е. слева направо.

person Prasoon Saurav    schedule 25.08.2011
comment
За исключением того, что я не думаю, что это применимо, если && или || перегружены. - person Omnifarious; 25.08.2011