Итак, почему я = ++ я + 1 четко определен в С++ 11?

Я видел другие похожие вопросы и читал defect об этом. Но я все еще не понимаю. Почему i = ++i + 1 четко определен в С++ 11, когда i = i++ + 1 нет? Как стандарт четко определяет это?

По моей работе у меня есть следующий секвенсор перед графиком (где стрелка представляет собой упорядоченную до отношения, и все это вычисление значения, если не указано иное):

i = ++i + 1
     ^
     |
assignment (side effect on i)
 ^      ^
 |      |
☆i   ++i + 1
     ||    ^
    i+=1   |
     ^     1
     |
★assignment (side effect on i)
  ^      ^
  |      |
  i      1

Я отметил побочный эффект i с черной звездой и вычислением значения i с белой звездой. Они кажутся непоследовательными по отношению друг к другу (согласно моей логике). И в стандарте говорится:

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

Объяснение в отчете не помогло мне понять. С чем связано преобразование lvalue-to-rvalue? Что я получил неправильно?

Ответ 1

... или вычисление значения , используя значение того же скалярного объекта...

Важная часть выделена жирным шрифтом. Левая сторона не использует значение i для вычисления значения. То, что вычисляется, является значением glvalue. Только после этого (после последовательности) значение объекта коснется и заменяется.

К сожалению, это очень тонкая точка:)

Ответ 2

Итак, ключевым моментом здесь является то, что результатом ++i является lvalue. И чтобы участвовать в двоичном +, значение lvalue должно быть преобразовано в rvalue путем преобразования lvalue-to-rvalue. Преобразование Lvalue-to-rvalue - это в основном акт чтения переменной i из памяти.

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

Это дополнительное секвенирование - это то, что непреднамеренно создало поведение, определенное в этом случае.

В С++ 03 не было строгого требования получить значение ++i непосредственно из i. Новое значение могло быть предсказано/предустановлено как i + 1 и использовано как операнд двоичного + еще до того, как оно было физически сохранено в фактическом i. Хотя можно утверждать, что требование было неявно там даже в С++ 03 (и тот факт, что С++ 03 не признал его существование, был дефектом С++ 03)


В случае i++ результатом является r-значение. Поскольку он уже является rvalue, нет никакого преобразования lvalue-to-rvalue и нет абсолютно никакого требования хранить его в i, прежде чем мы начнем оценивать двоичный +.

Ответ 3

С тех пор, как я задал этот вопрос, я написал статью о визуализацию секвенирования оценки С++ с помощью графиков. Этот вопрос идентичен случаю i = ++i, который имеет следующий граф с чередованием:

Sequenced-before graph for i = ++i

Красные узлы представляют собой побочные эффекты на i. Синий node представляет оценку, которая использует значение i. Причина, по которой это выражение хорошо определено, состоит в том, что ни один из этих узлов не является нелогичным друг с другом. Использование i слева от привязки не использует значение i, поэтому это не проблема.

Основная часть, которую я отсутствовала, - это то, что означает "использует значение". Оператор использует значение своего операнда, если он ожидает выражение prvalue для этого операнда. При присвоении имени объекта, который является lvalue, lvalue должен пройти преобразование lvalue-to-rvalue, которое можно рассматривать как "чтение значения объекта". Назначение требует только lvalue для его левого операнда, а это означает, что он не использует его значение.