Недавно в интервью был задан следующий вопрос объективного типа.
int a = 0;
cout << a++ << a;
Ответы:
а. 10
б. 01
с. неопределенное поведение
Я ответил на вариант b, т.е. вывод будет «01».
Но, к моему удивлению, позже интервьюер сказал мне, что правильный ответ - вариант c: undefined.
Теперь я знаю концепцию точек последовательности в C ++. Поведение не определено для следующего оператора:
int i = 0;
i += i++ + i++;
но в моем понимании для заявления cout << a++ << a
, то ostream.operator<<()
можно было бы назвать дважды, сначала ostream.operator<<(a++)
и позже ostream.operator<<(a)
.
Я также проверил результат на компиляторе VS2010, и его вывод тоже «01».
10
, было бы либо01
или00
. (c++
всегда будет оценивать значение, котороеc
было до увеличения). И даже если бы он не был неопределенным, это все равно было бы ужасно запутанным.Ответы:
Вы можете думать о:
Так как:
C ++ гарантирует, что все побочные эффекты предыдущих оценок будут выполнены в точках последовательности . Между оценками аргументов функции нет точек последовательности, что означает, что аргумент
a
может быть оценен доstd::operator<<(std::cout, a++)
или после аргумента . Таким образом, результат вышеизложенного не определен.Обновление C ++ 17
В C ++ 17 правила были обновлены. В частности:
Это означает , что он требует код в результате продукции
b
, которая выводит01
.Для получения дополнительных сведений см. P0145R3 Порядок оценки уточняющих выражений для идиоматического C ++ .
источник
c
есть типint
,operator<<
здесь представлены функции-члены.operator<<
функция-член или отдельная функция не влияет на точки последовательности.So the result of the above is undefined.
Ваше объяснение подходит только для неопределенных , но не для неопределенных . JamesKanze объяснил , как это более убийственный неопределенный в своем ответе , хотя .Технически в целом это неопределенное поведение .
Но есть два важных аспекта ответа.
Заявление кода:
оценивается как:
Стандарт не определяет порядок оценки аргументов функции.
Итак, Либо:
std::operator<<(std::cout, a++)
оценивается в первую очередь илиa
оценивается в первую очередь илиЭтот порядок не указан [ссылка 1] согласно стандарту.
[Ссылка 1] C ++ 03 5.2.2 Вызов функции,
пункт 8
Кроме того, нет точки последовательности между оценкой аргументов функции, но точка последовательности существует только после оценки всех аргументов [Ссылка 2] .
[Ссылка 2] C ++ 03 1.9 Выполнение программы [intro.execution]:
Пункт 17:
Обратите внимание, что здесь к значению
c
обращаются более одного раза без промежуточной точки последовательности, относительно этого в стандарте говорится:[Ссылка 3] C ++ 03 5 Выражения [expr]:
Пункт 4:
Код изменяется
c
более одного раза без вмешательства точки последовательности, и к нему не осуществляется доступ для определения значения сохраненного объекта. Это явное нарушение вышеупомянутого пункта и, следовательно, результатом, предписываемым стандартом, является Undefined Behavior [Ref 3] .источник
Точки последовательности определяют только частичный порядок. В вашем случае у вас есть (после разрешения перегрузки):
Между ними
a++
и первым вызовомstd::ostream::operator<<
есть точка последовательности, а между вторымa
и вторым вызовомstd::ostream::operator<<
есть точка последовательности, но нет точки последовательности междуa++
иa
; единственные ограничения порядка - это то, что ониa++
должны быть полностью оценены (включая побочные эффекты) перед первым вызовомoperator<<
, и что второеa
должно быть полностью оценено перед вторым вызовомoperator<<
. (Существуют также причинные ограничения порядка: второй вызовoperator<<
не может предшествовать первому, так как он требует в качестве аргумента результатов первого.) В §5 / 4 (C ++ 03) говорится:Один из допустимых упорядочений вашего выражения
a++
,a
, первый вызовoperator<<
, второй вызов кoperator<<
; это изменяет сохраненное значениеa
(a++
) и обращается к нему, кроме определения нового значения (второгоa
), поведение не определено.источник
c
был типом пользователя с определенным пользователем++
, вместо тогоint
, результаты были бы не определены, но не было бы неопределенным поведения.c
вfoo(foo(bar(c)), c)
? Есть точка последовательности, когда функции вызываются и когда они возвращаются, но не требуется вызова функции между их оценкамиc
.c
был UDT, перегруженные операторы были бы вызовами функций и вводили бы точку последовательности, поэтому поведение не было бы неопределенным. Но все равно не будет указано, было лиc
вычислено подвыражение до или послеc++
, поэтому не будет указываться, получили ли вы увеличенную версию или нет (и теоретически не обязательно должно быть одинаковым каждый раз).c
иc++
, поэтому они могут встречаться в любом порядке. Что касается точек с запятой ... Они вызывают точку последовательности только в том случае, если являются полными выражениями. Другие важные моменты последовательности вызова функции:f(c++)
будет увидеть приращениеc
вf
, а оператор запятой&&
,||
а?:
также причина точек последовательности.Правильный ответ - поставить под вопрос вопрос. Утверждение неприемлемо, потому что читатель не видит четкого ответа. Другой способ взглянуть на это состоит в том, что мы ввели побочные эффекты (c ++), которые значительно усложняют интерпретацию этого оператора. Краткий код - это здорово, если его смысл ясен.
источник