Стандарт C ++ 11 (5.17, expr.ass) утверждает, что
Во всех случаях присваивание последовательности после вычисления значения
правого и левого операндов, и до вычисления значения
выражение присваивания. По отношению к
неопределенно-последовательный вызов функции, операция соединения
задание является одной оценкой
Насколько я понимаю, все выражения, являющиеся частью данного присваивания, будут оцениваться до самого присваивания. Это правило должно работать, даже если я изменяю одну и ту же переменную дважды в одном и том же присваивании, что, я уверен, ранее было неопределенным поведением.
Будет ли данный код:
int a = 0;
a = (a+=1) = 10;
if ( a == 10 ) {
printf("this is defined");
} else {
printf("undefined");
}
всегда оценивать a==10
?
Давайте перепишем ваш код как
E1 = (E2 = E3)
где E1 — выражение a
, E2 это выражение a += 1
и E3 это выражение 10
, Здесь мы использовали, что оператор присваивания группируется справа налево (§5.17 / 1 в C ++ 11 Standard).
Более того, §5.17 / 1 гласит:
Во всех случаях присваивание выполняется после вычисления значения правого и левого операндов и до вычисления значения выражения присваивания.
Применение этого к нашему выражению означает, что мы сначала должны оценить подвыражения E1
а также E2 = E3
, Обратите внимание, что между этими двумя оценками нет отношения «секвенировано до», но это не вызывает проблем.
Оценка 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
является lvalue значения 10
,
Наконец, выражение значения E1
относится к заменяется значением выражения E2 = E3
, который мы рассчитали, чтобы быть 10
, Таким образом, переменная a
заканчивается, чтобы содержать значение 10
,
Поскольку все эти шаги четко определены, все выражение дает четко определенное значение.
Да, произошло изменение между C ++ 98 и C ++ 11. Я полагаю, что ваш пример хорошо определен в правилах C ++ 11, но демонстрирует неопределенное поведение в соответствии с правилами C ++ 98.
В качестве более простого примера, x = ++x;
не определено в C ++ 98, но хорошо определено в C ++ 11. Обратите внимание, что x = x++;
все еще не определено (побочный эффект постинкремента не секвенируется с оценкой выражения, в то время как побочный эффект предварительного инкремента секвенируется перед тем же).
Проведя небольшое исследование, я убедился, что поведение вашего кода хорошо определено в C ++ 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
(Поскольку его значение может быть вычислено только после побочного эффекта).
То же самое верно для второго и третьего назначения.
Смотрите также это отличный ответ, что объясняет отношение «секвенированный перед» гораздо более подробно и намного лучше, чем я мог.