«Чтение» результата преинкремента POD не приводит к неопределенному поведению. Почему именно? - PullRequest
7 голосов
/ 10 марта 2010

Это глупый вопрос. :)

[РЕДАКТИРОВАТЬ: глупо или нет, вопрос об особенностях C ++, см. UPDATE_2]

Предположим, у нас есть:

int a = 0; // line 1
int b = ++a; // line 2

В строке 2 происходит следующее ( обратите внимание, числа являются просто маркерами и не указывают точный порядок ):

                      = [1: write result of (3) to result of (2)]
                     /\
[2: take "b" l-value] [3: convert result of (4) to an r-value ]
                      |
                      [4: take "a" l-value, "increment" and return it]

«Запись» в (4) «упорядочивается» перед «чтением» в (3), и поскольку между ними нет последовательностей, побочный эффект не гарантируется до (3) (есть также "read" внутри (4), но упорядоченный перед"write", так что не дает UB).

Так где же ошибка в вышеприведенном?

[ОБНОВЛЕНИЕ, предназначенное для неопытных юристов, занимающихся секвенированием:)]

Другими словами, проблема в следующем:

  1. Похоже, существует "конкуренция", независимо от того, имеет ли место побочный эффект преобразования l-значения в r-значение ("чтение") или приращения ("запись").

  2. В C это даст UB , в соответствии с JTC1 / SC22 / WG14 N926 "Анализ точки последовательности" * (см., Например, ПРИМЕР 5: int x,y; (x=y) + x; // UB).

  3. Обратите внимание, что это не будет иметь место, если постинкремент будет использоваться, так как (3) и (4) будут составлять единственное [(3): возьмите l-значение l, преобразуйте его к r-значению и возвращают это r-значение] с побочным эффектом «записи», отложенным до тех пор, пока где-то до следующей точки последовательности

_

(*) Это выглядит как самое чистое систематическое обоснование темы, заданной членами Комитета по стандартам C99.

[UPDATE_2]

  1. Извлеченный урок: никогда не судите C ++ по правилам C :)). Я сделал именно это, задаваясь вопросом, почему N926 (который четко описывает образ вещей C99) был «недостаточно ясен» по теме предварительных инкрементов, дающих l-значения.

  2. Возникает вопрос: как построить аналогичное обоснование для C ++, если его нет, поскольку даже в случае C просто интерпретировать стандарт довольно сложно, а стандарт языка C ++ гораздо сложнее? и неясный.

[UPDATE_3]

Существует обсуждение, касающееся некоторых соответствующих тем (по крайней мере, в ~ более новой половине) на "Неопределенность общего выражения". , плюс вопрос обсуждается членами комитета здесь (см. «222. Точки последовательности и операторы, возвращающие значение»).

Ответы [ 2 ]

2 голосов
/ 11 марта 2010

Я думаю, что решение может быть в формулировке "++ i". Он говорит: «Значение - это новое значение операнда; это lvalue». И поведение не определено в 5/4 следующим образом: «Кроме того, доступ к предыдущему значению возможен только для определения значения, которое будет сохранено».

Итак, мы обращаемся не к предыдущему, а к новому значению. И тогда мы можем быть в порядке. Кажется, что это очень тонкая грань между неопределенным поведением и определенным поведением.

На самом деле «предыдущее значение» звучит для меня как «значение, которое объект имел в предыдущей точке последовательности». И если интерпретировать так, то эта конструкция выглядит неопределенной. Но если мы напрямую сравниваем формулировку «++ i» в 5.3 / 2 с 5/4, мы сталкиваемся с «новым значением» и «предыдущим значением», и вещи «склоняются» к определенному поведению («++ i msgstr "посмотрел бы значение" i "в следующей точке последовательности и выдаст это значение как содержимое результирующего lvalue" ++ i ").

1 голос
/ 11 марта 2010

Основное предложение в C ++ 5/4:

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

(И предложение, приведенное Йоханнесом, следующее, подчиненное.)

Как вы думаете, для какого скалярного объекта его сохраненное значение изменено несколько раз? a и b изменяются один раз в строке 2, поэтому проблем нет.

(Подобный язык в 6.5 / 2 в стандарте C.)


EDIT:

«Запись» в (4) «упорядочивается» перед «чтением» в (3), и поскольку между ними нет последовательностей, побочный эффект не гарантируется до (3)

Перечитывая ваш вопрос, я думаю, что путаница возникла из-за запутанного представления о ++a: выражение на самом деле не смотрит на будущую ценность a. Скорее, это может помочь воспринимать ++a как «return a+1 и как приращение побочного эффекта a, на досуге (до следующей точки последовательности)».

Так кого волнует, будет ли этот побочный эффект до или после (3)? Значение выражения, которое подается в (3), уже определено.

...