Вопрос по undefined-behavior, c++03, c++, c++11, language-lawyer – В каких версиях стандарта C ++ поведение «(i + = 10) + = 10» не определено?

32

В C ++ имеет ли неопределенное поведение следующее:

int i = 0;
(i+=10)+=10;

В комментариях кмой ответ вКаков результат + = в C и C ++? Тонкость здесь в том, что ответ по умолчанию выглядит как «да», тогда как кажется, что правильный ответ - «это зависит от версии стандарта C ++».

Если это зависит от версии стандарта, пожалуйста, объясните, где это UB, а где нет.

@ Фанаэль: оба. Чем полнее ответ, тем лучше. NPE
@JohnDibling: я так понимаю, вы думаете, что ответ "да". В комментариях к довольно убедительному аргументуstackoverflow.com/a/10653994/367273 что ответ на самом деле «нет». NPE
Какой C ++, текущий или старый? Fanael
@OnorioCatenacci: купи ему спецификацию, тогда поговорим о снижении голосов. Fanael
Можно утверждать, что это вопрос FAQ о точках последовательности, который сам должен быть обновлен, чтобы отразить новые правила C ++ 11 с sequence-before и -after. Но я не думаю, что я готов утверждать, что, возможно, было бы лучше пометить существующий вопрос часто задаваемых вопросов как C ++ 03 и начать все сначала для C ++ 11. Steve Jessop

Ваш Ответ

3   ответа
20

i == 20.

От[expr.ass]/1:

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

Это означает, что назначениеi+=1 последовательность перед вычислением значения левой части(i+=10)+=10который, в свою очередь, упорядочен перед окончательным назначениемi.

В C ++ 03 выражение имеет неопределенное поведение, потому что оно вызываетi быть измененным дважды без промежуточной точки последовательности.

Спасибо - это действительно должно быть разъяснено в ответе, так как этот вопрос не определяет C ++ 11.
Относится ли этот ответ только к C ++ 11 или к C ++ в целом?
... может быть, стоит упомянуть, что, поскольку результатом i + = 10 является lvalue (т. е. «i» сам объект), модификация i не может рассматриваться как «независимая»; побочный эффект (который ничем не упорядочен до окончательного ";")
@PaulR: только C ++ 11. C ++ 03 не имел понятия «секвенирован после», он использовал точки последовательности. Фактически, в C ++ 03 он не определен, поскольку между назначениями нет промежуточной точки последовательности.
@PaulR В C ++ 98 абзац гласит: «Результатом операции присваивания является значение, сохраненное в левом операнде после того, как присваивание заняло ta, ken place;» Я не уверен, технически ли это означает, что существует точка последовательности между назначениями. По крайней мере, я думаю, что это недостаточно указано в C ++ 98.
34

tl;dr:sequence модификаций и чтений, выполненных в(i+=10)+=10 хорошо определено как в C ++ 98, так и в C ++ 11, однако в C ++ 98 этого недостаточно для определения поведения.

В C ++ 98 несколько модификаций одного и того же объекта без вмешательстваsequence-point приводит к неопределенному поведению, даже когда порядок этих модификаций четко указан. Это выражение не содержит никаких точек последовательности, и поэтому факт, что он состоит из двух модификаций, достаточен, чтобы сделать его поведение неопределенным.

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

Поэтому поведение не определено в C ++ 98, но хорошо определено в C ++ 11.

C++98

C ++ 98 пункт [expr] 5 p4

Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expression, and the order in which side effects take place, is unspecified.

C ++ 98 пункт [expr.ass] 5.17 p1

The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue

Поэтому я считаю, что порядок указан, однако я не вижу, что одного этого достаточно, чтобы создать точку последовательности в середине выражения. И продолжаем с цитатой из [expr] 5 p4:

Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression.

Поэтому, даже если порядок указан, мне кажется, что этого недостаточно для определенного поведения в C ++ 98.

C++11

C ++ 11 убирает точки последовательности для более ясного представления оsequence-before а такжеsequenced-after, Язык из C ++ 98 заменен на

C ++ 11 [intro.execution] 1,9 стр. 15

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...]

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

C ++ 11 [expr.ass] 5.17 p1

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

Таким образом, хотя упорядочения было недостаточно для того, чтобы поведение, определенное в C ++ 98, C ++ 11 изменило требование так, что упорядочения (то есть упорядоченности) достаточно.

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

Мне кажется маловероятным, что любая реализация C ++ 98 действительно сделает что-то удивительное, когда последовательность операций четко задана, даже если этого недостаточно для создания технически хорошо определенного поведения. Например, внутреннее представление этого выражения, созданного Clang в режиме C ++ 98, имеет четко определенное поведение и выполняет ожидаемые действия.

сразу после цитируемого вами текста, заканчивающегося символом "...", его сохраненное значение будет изменено не более одного раза при оценке выражения. "
@ JohannesSchaub-litb Однако, если предполагаемое значение является «только предыдущее значение должно быть доступно» то означает ли это, что «нет необходимости обращаться к предыдущему значению» только для определения значения, которое должно быть сохранено? » То есть я могу получить доступ к предыдущему значению для других целей?
& quot; тогда как в C ++ 98 значение, присвоенное j, является неопределенным, но поведение не является неопределенным & quot; - это неправильно. C ++ 98 говорит, что предыдущее значение «i» должны считываться только для определения значения, которое должно быть сохранено в модификации, когда нет промежуточной точки последовательности. Но в «(i + = 1) + i» не указано, является ли «+ i»; считывает предыдущее или следующее значение "i", так что следствием этого является неопределенное поведение.
@ JohannesSchaub-litb Не могли бы вы дать мне цитату, чтобы я мог видеть ее в стандарте C ++ 98 в контексте?
@ JohannesSchaub-litb Я думаю, что вы, вероятно, правы, что этот язык должен был означать это, и что требование, которое я считал новым в C ++ 11, на самом деле не ново. Но язык C ++ 98 определенно нуждался в очистке, потому что, как написано, «предыдущее значение должно быть доступно только для [...],» не означает то же самое, что и "только предшествующее значение должно быть доступно".
13

В C ++ 03 это очевидный UB, между назначениями нет промежуточной точки последовательности.

В C ++ 11, как объясняет Манкарс, это больше не является неопределенным & # x2014; составное назначение в скобках упорядочено перед внешним, так что все в порядке.

@Mankarse: Теперь этоgreat вопрос. NPE
@Mankarse: предположительно для согласованности с простым заданием, так что вы можете сделать обаf(x = 5) а такжеf(x += 5) сf желаяT&.
@Mankarse Данное обоснование (с которым я действительно не согласен) заключается в поддержке таких вещей, какint& f(int& i) { return i += 2; }
Ответ C ++ 03 поднимает новый вопрос - почему оператор присваивания возвратил lvalue в C ++ 03, если он действительно не был определен для изменения своего результата?
@Fanael: я знаю, что ты только догадываешься, но я не следую твоей логике. Если операторы присваивания не вернулиlvalueтогда ниf(x = 5) ниf(x += 5) будет компилировать (дляf желаяint&). Как это будет менее последовательным?

Похожие вопросы