Двойное присвоение одной переменной в одном выражении в С++ 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 (поскольку его значение может быть вычислено только после побочного эффекта).

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

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