Двойное присвоение одной переменной в одном выражении в С++ 11
В стандарте С++ 11 (5.17, expr.ass) указано, что
Во всех случаях назначение упорядочивается после вычисления значения правого и левого операндов и перед вычислением значения выражение присваивания. Что касается неопределенно-секвенированный вызов функции, работа соединения назначение - это единая оценка
Как я понимаю, все выражения, которые являются частью данного присваивания, будут оцениваться до самого присваивания. Это правило должно работать, даже если я дважды изменяю одну и ту же переменную в одном присваивании, что, я уверен, было раньше undefined.
Будет ли данный код:
int a = 0;
a = (a+=1) = 10;
if ( a == 10 ) {
printf("this is defined");
} else {
printf("undefined");
}
всегда оценивается до a==10
?
Ответы
Ответ 1
Перепишите свой код как
E1 = (E2 = E3)
где E1 - выражение a
, E2 - выражение a += 1
, а E3 - выражение 10
. Здесь мы рассмотрели, что оператор присваивания имеет право справа налево (§5.17/1 в стандарте С++ 11).
§5.17/1 также утверждает:
Во всех случаях назначение выполняется после вычисления значения правого и левого операндов и перед вычислением значения выражения присваивания.
Применяя это к нашему выражению, мы сначала должны оценить подвыражения E1
и E2 = E3
. Обратите внимание, что между этими двумя оценками нет отношения "sequenced-before", но это не создает проблем.
Оценка id-выражения E1
тривиальна (результат сам a
). Оценка выражения-назначения E2 = E3
выполняется следующим образом:
Сначала необходимо оценить оба подвыражения. Оценка литерала E3
снова тривиальна (дает значение значения 10).
Оценка выражения (составного) присваивания E2
выполняется в следующих шагах:
1) Поведение a += 1
эквивалентно a = a + 1
, но a
оценивается только один раз (§5.17/7). После оценки подвыражений a
и 1
(в произвольном порядке) преобразование lvalue-rvalue применяется к a
, чтобы прочитать значение, сохраненное в a
.
2) Добавлены значения a
(0
) и 1
(a + 1
), и результатом этого добавления является значение значения 1
.
3) Прежде чем мы сможем вычислить результат присваивания a = a + 1
, значение объекта, на которое ссылается левый операнд, заменяется значением правильного операнда (§5.17/2). Результатом E2
является тогда значение lvalue, относящееся к новому значению 1
. Обратите внимание, что побочный эффект (обновление значения левого операнда) секвенирован перед вычислением значения выражения присваивания. Это §5.17/1, процитированное выше.
Теперь, когда мы оценили подвыражения E2
и E3
, значение выражения E2
означает, что оно заменяется значением E3
, которое равно 10
. Следовательно, результатом E2 = E3
является l значение значения 10
.
Наконец, выражение значения E1
означает, что оно заменяется значением выражения E2 = E3
, которое мы вычислили как 10
. Таким образом, переменная a
заканчивается, чтобы содержать значение 10
.
Так как все эти шаги хорошо определены, все выражение дает четко определенное значение.
Ответ 2
Да, произошли изменения между С++ 98 и С++ 11. Я считаю, что ваш пример хорошо определен в соответствии с правилами С++ 11, демонстрируя поведение undefined в соответствии с правилами С++ 98.
В качестве более простого примера x = ++x;
является undefined в С++ 98, но хорошо определен в С++ 11. Обратите внимание, что x = x++;
по-прежнему undefined (побочный эффект пост-инкремента не зависит от оценки выражения, тогда как побочный эффект предварительного инкремента секвенирован до того же самого).
Ответ 3
После небольшого исследования, я убежден, что поведение ваших кодов хорошо определено в С++ 11.
$1.9/15:
Вычисления значений операндов оператора секвенированы до вычисление значения результата оператора.
$5.17/1:
Оператор присваивания (=
) и операторы присваивания всех групп справа налево.
Если я правильно понял, в вашем примере
a = (a+=1) = 10;
это означает, что вычисления значений (a+=1)
и 10
должны быть выполнены перед вычислением значения (a+=1) = 10
, а вычисление значения этого выражения должно быть закончено до того, как будет оценено значение a = (a+=1) = 10;
.
$5.17/1:
Во всех случаях назначение выполняется после вычисления значения правого и левого операндов и перед вычислением значения выражения присваивания.
Это означает, что назначение должно происходить перед вычислением значения, и поэтому из-за транзитивности оценка (a+=1) = 10
может начинаться только после назначения a+=1
(поскольку его значение может быть вычислено только после побочного эффекта).
То же самое верно для второго и третьего назначений.
Смотрите также этот отличный ответ, в котором объясняется взаимосвязь с последовательностью до гораздо более подробно и лучше, чем я мог.