Подтвердить что ты не робот

Каков правильный ответ для cout <<a ++ <<a;?

Недавно в интервью был задан следующий объективный вопрос.

int a = 0;
cout << a++ << a;

Ответы:

а. 10
б. 01
с. undefined поведение

Я ответил на выбор b, т.е. выход был бы "01".

Но, к моему удивлению, позже мне сказал интервьюер, что правильным ответом является вариант c: undefined.

Теперь я знаю понятие точек последовательности в С++. Поведение undefined для следующего утверждения:

int i = 0;
i += i++ + i++;

но согласно моему пониманию для выражения cout << a++ << a, ostream.operator<<() будет вызываться дважды, сначала с ostream.operator<<(a++) и позже ostream.operator<<(a).

Я также проверил результат на компиляторе VS2010, и его вывод также "01".

4b9b3361

Ответ 1

Вы можете думать:

cout << a++ << a;

Как

std::operator<<(std::operator<<(std::cout, a++), a);

С++ гарантирует, что все побочные эффекты предыдущих оценок будут выполняться в точках последовательности. Между аргументами аргументов функций нет точек последовательности, что означает, что аргумент a можно оценить до аргумента std::operator<<(std::cout, a++) или после. Таким образом, результат выше undefined.


Обновление С++ 17

В С++ 17 правила были обновлены. В частности:

В выражении оператора сдвига E1<<E2 и E1>>E2 каждое вычисление значения и побочный эффект E1 секвенируется перед каждым вычислением значения и побочным эффектом E2.

Это означает, что для этого требуется, чтобы код выдавал результат b, который выводит 01.

Подробнее см. P0145R3 для уточнения порядка оценки выражений для Idiomatic С++.

Ответ 2

Технически, в целом это Undefined Поведение.

Но есть два важных аспекта ответа.

Оператор кода:

std::cout << a++ << a;

оценивается как:

std::operator<<(std::operator<<(std::cout, a++), a);

Стандарт не определяет порядок оценки аргументов функции.
Так что:

  • std::operator<<(std::cout, a++) оценивается сначала или
  • a оценивается сначала или
  • это может быть любой определенный порядок реализации.

Этот порядок Unspecified [Ref 1] в соответствии со стандартом.

[Ссылка 1] С++ 03 5.2.2 Вызов функции
Параграф 8

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

Кроме того, нет никакой точки последовательности между оценкой аргументов функции, но точка последовательности существует только после оценки всех аргументов [Ref 2].

[Ref 2] С++ 03 1.9 Выполнение программы [intro.execution]:
Параграф 17:

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

Обратите внимание, что здесь значение c обращается более одного раза без промежуточной точки последовательности, в связи с чем в стандарте указано:

[Ссылка 3] С++ 03 5 Выражения [expr]:
Параграф 4:

....
Между предыдущей и следующей точками последовательности скалярный объект должен иметь значение хранимого значения, измененное не более одного раза путем оценки выражения. Кроме того, к предыдущему значению следует обращаться только для определения значения, которое нужно сохранить. Требования настоящего параграфа выполняются для каждого допустимого упорядочения подвыражений полного выражение; в противном случае поведение undefined.

Код изменяет c более одного раза без промежуточной точки последовательности и к нему не обращаются, чтобы определить значение сохраненного объекта. Это явное нарушение вышеприведенного предложения, и поэтому результат, указанный в стандарте, Undefined Поведение [Ссылка 3].

Ответ 3

Точки последовательности определяют только частичное упорядочение. В вашем случае у вас есть (после выполнения разрешения перегрузки):

std::cout.operator<<( a++ ).operator<<( a );

Существует точка последовательности между a++ и первым вызовом std::ostream::operator<<, и есть точка последовательности между второй a и второй вызов std::ostream::operator<<, но там не является точкой последовательности между a++ и a; единственный заказ ограничения заключаются в том, что a++ будет полностью оценена (включая побочные эффекты) перед первым вызовом operator<< и чтобы второй a был полностью оценивается перед вторым вызовом operator<<. (Это также причинно-следственные ограничения: второй вызов operator<< не может предшествуют первому, поскольку для этого требуются результаты первого, как аргумент.) В §5/4 (С++ 03) указано:

За исключением случаев, когда указано, порядок оценка операндов отдельных операторов и подвыражений отдельные выражения и порядок, в котором происходят побочные эффекты, не указано. Между предыдущей и следующей точкой последовательности скаляр объект должен иметь свою сохраненную ценность, измененную не более чем одним раз оценка выражения. Кроме того, предыдущее значение должно быть доступ только для определения значения, которое необходимо сохранить. Требования этот пункт должен соблюдаться для каждого допустимого порядка подвыражения полного выражения; в противном случае поведение undefined.

Один из допустимых порядков вашего выражения: a++, a, сначала вызов operator<<, второй вызов operator<<; это изменяет сохраненное значение a (a++), и обращается к нему, кроме как для определения новое значение (второе a), поведение undefined.

Ответ 4

Правильный ответ заключается в вопросе вопроса. Утверждение неприемлемо, потому что читатель не может видеть ясный ответ. Другой способ взглянуть на это состоит в том, что мы внедрили побочные эффекты (С++), которые усложняют интерпретацию выражения. Краткий код велик, при условии, что это значение ясно.