Вычисления нецелого значения (точки последовательности a.k.a)

Извините за то, что вы снова открыли эту тему, но, думая об этой теме, я начал давать мне Undefined Behavior. Хотите переместиться в зону четко определенного поведения.

Учитывая

int i = 0;
int v[10];
i = ++i;     //Expr1
i = i++;     //Expr2
++ ++i;      //Expr3
i = v[i++];  //Expr4

Я думаю о вышеупомянутых выражениях (в этом порядке) как

operator=(i, operator++(i))    ; //Expr1 equivalent
operator=(i, operator++(i, 0)) ; //Expr2 equivalent
operator++(operator++(i))      ; //Expr3 equivalent
operator=(i, operator[](operator++(i, 0)); //Expr4 equivalent

Приступая к поведению здесь важны цитаты из С++ 0x.

$1.9/12- "Оценка выражения (или подвыражение) в целом включает в себя как вычисления значений (включая определение личности объект для оценки lvalue и значение fetchinga, ранее присвоенное объект для оценки rvalue) и инициирование побочных эффектов."

$1.9/15- "Если побочный эффект скаляра объект не зависит от либо другой побочный эффект на тот же скалярный объект или значение вычисление с использованием значения тот же скалярный объект, поведение undefined".

[Примечание: вычисление значения и сторона эффекты, связанные с различными выражения аргументов не имеют никакого значения. -end note]

$3.9/9- "Арифметические типы (3.9.1), типы перечислений, типы указателей, указатель на типы членов (3.9.2), std:: nullptr_t и cv-qual версии этих типов (3.9.3) все вместе называются скалярными типами.

  • В Expr1 оценка выражения i (первый аргумент) не влияет на оценку истечения operator++(i) (которая имеет побочный эффект).

    Следовательно, Expr1 имеет поведение Undefined.

  • В Expr2 оценка выражения i (первый аргумент) не влияет на оценку истечения operator++(i, 0) (который имеет побочный эффект).

    Следовательно, Expr2 имеет поведение Undefined.

  • В Expr3 оценка одиночного аргумента operator++(i) должна быть полной до того, как вызывается внешний operator++.

    Следовательно, Expr3 имеет хорошо определенное поведение.

  • В Expr4 оценка выражения i (первый аргумент) не влияет на оценку operator[](operator++(i, 0) (которая имеет побочный эффект).

    Следовательно, Expr4 имеет поведение Undefined.

Правильно ли это понимание?


P.S. Метод анализа выражений, как в OP, неверен. Это связано с тем, что, поскольку @Potatoswatter, примечания - "пункт 13.6 не применяется, см. Отказ от ответственности в 13.6/1", эти кандидатские функции участвуют в процессе разрешения перегрузки оператора, как описано в 13.3.1.2, и используются для других целей. "Это просто фиктивные декларации, не существует семантики функциональных вызовов в отношении встроенных операторов".

Ответ 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.

Ответ 2

В мышлении о выражениях, подобных тем, которые упомянуты, я считаю, что полезно представить машину, где память имеет блокировки, так что чтение места памяти как части последовательности чтения-изменения-записи вызовет любые попытки чтения или записи, отличные от завершая запись последовательности, которая будет остановлена ​​до тех пор, пока последовательность не завершится. Такая машина вряд ли была бы абсурдной концепцией; действительно, такая конструкция может упростить многие многопоточные сценарии кода. С другой стороны, выражение типа "x = y ++;" может выйти из строя на такой машине, если "x" и "y" были ссылками на одну и ту же переменную, а код сгенерированный компилятором сделал что-то вроде read-and-lock reg1 = y; reg2 = REG1 + 1; написать x = reg1; write-and-unlock y = reg2. Это было бы очень разумной кодовой последовательностью на процессорах, где запись вновь вычисленного значения налагала бы задержку конвейера, но запись в x блокировала бы процессор, если бы y были сглажены с той же переменной.