Неожиданный порядок оценки (ошибка компилятора?)

Возможный дубликат:
Неопределенные поведенческие и последовательные точки

Я не уверен, что это ошибка gcc или нет, поэтому я спрошу:

unsigned int n = 0; std::cout << n++ << n << ++n; 

gcc дает чрезвычайно странный результат: «122», который AFAICT невозможно. Поскольку << является ассоциативным, он должен быть таким же, как:

 operator<<(operator<<(operator<<(std::cout, n++), n), ++n) 

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

Таким образом, действительные результаты AFAICT будут следующими: 111 012 002 101

и ничего больше

Существует точка последовательности между оценкой аргументов и вызовом функции. Между оценкой разных аргументов нет точки последовательности.

Давайте посмотрим на внешний вызов функции:

 operator<<(operator<<(operator<<(std::cout, n++), n), ++n) 

Аргументы:

  • operator<<(operator<<(std::cout, n++), n)

а также

  • ++n

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

Из стандарта, раздел [intro.execution] (формулировка из проекта 3225):

  • Если A не секвенирован до того, как B и B не секвенированы до A , то A и B не подвержены влиянию . [ Примечание . Выполнение неизученных оценок может перекрываться. - конечная нота ]

  • За исключением тех случаев, когда отмечено, оценки операндов отдельных операторов и подвыражений отдельных выражений не имеют последствий. [ Примечание. В выражении, которое оценивается более одного раза во время выполнения программы, необратимые и неопределенно упорядоченные оценки его подвыражений не обязательно должны выполняться последовательно при различных оценках. - end note ] Вычисления значений операндов оператора секвенируются перед вычислением значения результата оператора. Если побочный эффект на скалярном объекте не влияет на какой-либо другой побочный эффект на один и тот же скалярный объект или вычисление значения с использованием значения одного и того же скалярного объекта, поведение не определено.

Поскольку у вас есть несколько операций с побочными эффектами на одном и том же скалярном объекте, которые не зависят друг от друга, вы находитесь в этой области неопределенного поведения, и даже 999 будет допустимым выходом.

Первое правило ошибок компилятора: вероятно, это не ошибка компилятора, а недоразумение с вашей стороны. Использование операторов postfix и prefix в том же самом выражении приводит к неопределенному поведению. Попробуйте использовать параметр -Wall чтобы дать вам больше предупреждений и показать потенциальные ошибки в вашем коде.

Давайте посмотрим, что GCC 4.2.1 сообщает нам, когда мы запрашиваем предупреждения о test.cpp :

 #include  int main() { unsigned int n = 0; std::cout << n++ << n << ++n << std::endl; return 0; } 

Когда мы компилируем:

 $ g++ -Wall test.cpp -o test test.cpp: In function 'int main()': test.cpp:5: warning: operation on 'n' may be undefined 

Ваш код является примером того, почему в некоторых книгах отмечают, что опытным программистам не нравится эта (++, -) перегрузка оператора, даже другие языки (ruby) не реализовали ++ или -.

Давайте будем гением компьютера.