Подтвердить что ты не робот

Почему поведение `i = я ++ + 1` undefined в С++ 11?

Я читаю n3290 черновик стандарта С++ 11 (как можно ближе к фактическому стандартному тексту), и я заметил, что i = i++ + 1; создает поведение undefined. Раньше я видел подобные вопросы, но на них отвечали более старые стандарты (точки последовательности). Новый стандарт вводит вместо этого концепцию секвенирования до/после отношения между выражениями и выполнением подвыражений.

1.9 13 Последовательность перед этим является асимметричным, транзитивным, парным отношением между оценками, выполненными одним потоком (1.10), который индуцирует частичный порядок среди этих оценок. При любых двух оценках A и B, если A секвенирован до B, то исполнение A должно предшествует исполнению B. Если A не секвенирован до того, как B и B не секвенированы до A, то A и B не подвержены влиянию. [ Обратите внимание выполнение необязательных оценок может перекрываться. -end note] Оценки A и B неопределенно секвенированы, когда либо A является секвенирован до того, как B или B секвенирован до A, но он не определен который. [Примечание: неопределенные последовательности оценок не могут перекрываться, но либо можно было выполнить в первую очередь. -end note]

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

1.9 15 За исключением случаев, когда отметили, что оценки операндов отдельных операторов и подвыражения отдельных выражений неощутимы. [Примечание: в выражение, которое оценивается более одного раза во время выполнения программа, необоснованные и неопределенно упорядоченные оценки его подвыражения не должны выполняться последовательно в разных оценки. -end note] Вычисления значений операндов оператор упорядочивается перед вычислением значения результата Оператор. Если побочный эффект скалярного объекта не подвержен относительно какого-либо другого эффекта на том же скалярном объекте или вычисление значения с использованием значения одного и того же скалярного объекта, поведение undefined.

[ Example:
void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
}
—end example ]

Как я понимаю, он работает так:

  • operator= имеет два выражения операндов: ссылаясь на i и i++ + 1, оба не привязаны друг к другу. Второй имеет побочный эффект на i, но первый кажется мне не иметь побочного эффекта или использоваться при вычислении значения (или ссылается на "вычисление значения с использованием значения одного и того же скалярного объекта"? на самом деле зависит от значения, хранящегося в i? не так думайте), так что это не поведение undefined;
  • operator= выполнение выполняется после обеих операндов. Он имеет побочный эффект на i, но он хорошо упорядочен по отношению к обоим операндам, так что это не udefined поведение;
  • i++ + 1 - это явно определенное поведение.

Я что-то не так? Или это поведение линии undefined по какой-то другой причине?

PS. Стандарт фактически говорит

Вычисления значений операндов оператора секвенированы перед вычислением значения результата оператора.

и он вообще не упоминает побочные эффекты в этом контексте. Однако отношение секвенирования определяется только между оценками выражений и оценкой = вычислением значения + побочными эффектами. Поэтому либо я должен предположить, что этот проект здесь несовместим, либо предположим, что в этой строке они означают оценку вместо вычисления стоимости. Или я здесь не прав?

EDIT:

Думаю, я отвечу на себя здесь, но в этом причина моей путаницы:

5 1 Выражение представляет собой последовательность операторов и операндов, которые задает вычисление. Выражение может привести к значению и может вызывают побочные эффекты.

Таким образом, операнды операторов не являются самими суб-выражениями. Поэтому вычисляется только вычисление значения для целых i = i++ + 1;, и не упоминается о секвенсировании побочных эффектов стандартом. Вот почему он undefined.

Обратите внимание, что если, например, operator= был перегружен для заданного типа (так что это будет вызов функции implicite), это не будет поведение undefined, правильно?

4b9b3361

Ответ 1

Это "undefined поведение", а не "неуказано". Undefined означает, что машине разрешено делать что угодно, включая вывод пустой программы, случайное прерывание или взорвать. Разумеется, более тонкое неожиданное значение при переносе на другую платформу является более вероятным результатом.

Undefined поведение применяется к любому случаю, когда два побочных эффекта применяются к одному и тому же скаляру, не будучи упорядоченным относительно друг друга. В этом случае побочные эффекты оказываются идентичными (оба прироста i от его исходного значения перед выражением), но по букве стандарта они объединяются для создания UB.

Непоследовательность побочных эффектов, поскольку помимо ,, ?:, || и &&, операторы не определяют правила секвенирования в таких терминах, как С++ 11 §5.15/2:

Если второе выражение оценивается, каждое вычисление значения и побочный эффект, связанные с первым выражением, секвенируются перед каждым вычислением значения и побочным эффектом, связанным со вторым выражением.

Операторы присваивания определяют специальное правило последовательности, §5.17/1:

Во всех случаях назначение выполняется после вычисления значения правого и левого операндов и перед вычислением значения выражения присваивания.

Это не помогает i = i ++ + 1, потому что побочный эффект i ++ не является частью вычисления каких-либо значений.

Ответ 3

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

Для контрастного примера взгляните на оператор запятой: "Каждое вычисление значения и побочный эффект, связанный с левым выражением, секвенируются перед каждым вычислением значения и побочным эффектом, связанным с правильным выражением". Обратите внимание, как вычисляются значения и побочные эффекты отдельно. Нет такого правила для назначения.