Ответ 1
Собственные операторные выражения не эквивалентны перегруженным операторным выражениям. Существует точка последовательности при привязке значений к аргументам функции, что делает версии operator++()
хорошо определенными. Но этого не существует для случая родного типа.
Во всех четырех случаях i
дважды изменяется в пределах полного выражения. Поскольку в выражениях нет ,
, ||
или &&
, этот момент UB.
§ 5/4:
Между предыдущей и следующей точками последовательности скалярный объект должен иметь значение, которое его хранимое значение изменялось не более одного раза путем оценки выражения.
Изменить для С++ 0x (обновлено)
§1.9/15:
Вычисления значений операндов оператора секвенируются перед вычислением значения результата оператора. Если побочный эффект на скалярном объекте не влияет на какой-либо другой побочный эффект на один и тот же скалярный объект или вычисление значения с использованием значения одного и того же скалярного объекта, поведение undefined.
Обратите внимание, однако, что вычисление значения и побочный эффект - две разные вещи. Если ++i
эквивалентно i = i+1
, то +
- вычисление значения, а =
- побочный эффект. От 1.9/12:
Оценка выражения (или подвыражения) в общем случае включает в себя как вычисления значений (включая определение идентичности объекта для оценки glvalue и получение значения, ранее назначенного объекту для оценки оценки), так и инициирование побочных эффектов.
Итак, хотя вычисления значений более строго секвенированы в С++ 0x, чем С++ 03, побочные эффекты не являются. Два побочных эффекта в том же выражении, если не указано иначе, производят UB.
В любом случае вычисления значений упорядочиваются по их зависимостям между данными, а побочные эффекты отсутствуют, их порядок оценки ненаблюдаем, поэтому я не уверен, почему С++ 0x не хочет ничего говорить, но это просто означает, что мне нужно больше читать статьи Бёма, и друзья писали.
Редактировать # 3:
Спасибо Йоханнесу за то, что он справился с моей лень, чтобы напечатать "упорядоченный" в моей панели поиска PDF-ридера. Я собирался спать и вставать на последние два изменения так или иначе... правильно, v).
§5.17/1, определяющий операторы присваивания, говорит
Во всех случаях назначение выполняется после вычисления значения правого и левого операндов и перед вычислением значения выражения присваивания.
Кроме того, в §5.3.2/1 по оператору preincrement говорится:
Если x не относится к типу bool, выражение ++ x эквивалентно x + = 1 [Примечание: см.... дополнение (5.7) и операторы присваивания (5.17)...].
Таким образом, ++ ++ x
является сокращением для (x +=1) +=1
. Итак, давайте интерпретировать это.
- Оцените
1
на дальнем RHS и спуститесь в parens. - Оцените внутренний
1
и значение (prvalue) и адрес (glvalue)x
. - Теперь нам нужно значение подвыражения + =.
- Мы закончили вычисление значений для этого подвыражения.
- Дополнительный эффект присваивания должен быть секвентирован до того, как будет доступно значение присвоения!
- Назначьте новое значение
x
, которое идентично результату glvalue и prvalue подвыражения. - Сейчас мы вышли из леса. Теперь все выражение уменьшено до
x +=1
.
Итак, тогда 1 и 3 четко определены, а 2 и 4 - поведение undefined, которое вы ожидаете.
Единственный сюрприз, который я обнаружил при поиске "sequinated" в N3126, был 5.3.4/16, где реализации разрешено вызывать operator new
перед оценкой аргументов конструктора. Это круто.
Редактировать # 4: (О, что мы сплетем в сети)
Йоханнес отмечает еще раз, что вi == ++i;
glvalue (a.k.a. адрес) i
неоднозначно зависит от ++i
. Значение glvalue, безусловно, является значением i
, но я не думаю, что 1.9/15 должен включать его по той простой причине, что glvalue именованного объекта является константой и на самом деле не может иметь зависимости.
Для информативного соломона рассмотрим
( i % 2? i : j ) = ++ i; // certainly undefined
Здесь glvalue LHS =
зависит от побочного эффекта от prvalue i
. Адрес i
не подлежит сомнению; результат ?:
равен.
Возможно, хороший контрпример -
int i = 3, &j = i;
j = ++ i;
Здесь j
имеет glvalue, отличный от (но идентичный) i
. Является ли это четко определенным, но i = ++i
нет? Это представляет собой тривиальное преобразование, которое компилятор может применить к любому случаю.
1.9/15 следует сказать
Если побочный эффект скалярного объекта не влияет на какой-либо другой побочный эффект на один и тот же скалярный объект или вычисление значения, используя prvalue того же скалярного объекта, поведение undefined.