Двойное присвоение одной и той же переменной в одном выражении в C ++ 11

Стандарт 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?

11

Решение

Давайте перепишем ваш код как

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,

Поскольку все эти шаги четко определены, все выражение дает четко определенное значение.

7

Другие решения

Да, произошло изменение между C ++ 98 и C ++ 11. Я полагаю, что ваш пример хорошо определен в правилах C ++ 11, но демонстрирует неопределенное поведение в соответствии с правилами C ++ 98.

В качестве более простого примера, x = ++x; не определено в C ++ 98, но хорошо определено в C ++ 11. Обратите внимание, что x = x++; все еще не определено (побочный эффект постинкремента не секвенируется с оценкой выражения, в то время как побочный эффект предварительного инкремента секвенируется перед тем же).

7

Проведя небольшое исследование, я убедился, что поведение вашего кода хорошо определено в 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 (Поскольку его значение может быть вычислено только после побочного эффекта).

То же самое верно для второго и третьего назначения.

Смотрите также это отличный ответ, что объясняет отношение «секвенированный перед» гораздо более подробно и намного лучше, чем я мог.

3
По вопросам рекламы [email protected]