22

В another answer было указано, что до C++ 11, где i является int, а затем использовать выражения:Does * & ++ i вызывает неопределенное поведение в C++ 03?

*&++i 

вызвало неопределенное поведение. Это правда?

На другом ответе была небольшая дискуссия в комментариях, но это кажется неубедительным.

+1

В этом ответе очень хорошо осведомленный «Johannes Schaub-litb» комментирует, что он считает, что это неверное утверждение. И не получает ответа. –

+2

@DrewDormann James Kanze также очень хорошо осведомлен –

+2

'* & i' был бы абсолютно прав, и' ++ i' возвращает ссылку на 'i', поэтому я сам не вижу проблему ... –

ответ

0

Я думаю, что вопрос имеет смысл только, если мы имеем дело с выражением:

i = *&++i; 

Соответствующая цитата в стандарте C++ 03 будет [выражение]/4:

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

i = ++i + 1; // the behavior is unspecified 

Мы можем просто сравнить секвенирование i = *&++i против i = ++i + 1, чтобы определить, что такое же правило вызывает как быть указан. Они оба утверждения вида:

i = f(++i); 

Для любой функции f, чтение i на левой стороне и побочный эффект от ++i на правой стороне не упорядочены по отношению друг Другие. Следовательно, неопределенное поведение.

7

Не имеет смысла спрашивать, имеет ли *&++i UB. Отсрочка не обязательно получает доступ к сохраненному значению (ранее или новому) i, как вы можете видеть, используя это как выражение инициализатора для ссылки. Только если участвует преобразование rvalue (использование в таком контексте), есть ли вообще вопрос для обсуждения. И тогда, поскольку мы можем использовать значение ++i, мы можем использовать значение *&++i с точно такими же предостережениями, как и для ++i.

Исходный вопрос касался в основном i = ++i, что аналогично i = *&++i. Это было неопределенное поведение в C++ 03, из-за того, что i дважды модифицировалось между точками последовательности и хорошо определено в C++ 11 из-за побочных эффектов оператора присваивания, секвенированного после вычислений значений слева и правой стороны.

Возможно, уместно отметить, что ненормативные примеры в стандартах C++ 98 и C++ 03 были неверными, описывая некоторые случаи формально неопределенного поведения как просто неопределенное поведение. Таким образом, намерение не было полностью ясным, на всем пути назад.Хорошее эмпирическое правило состоит в том, чтобы просто не полагаться на такие неясные угловые случаи языка, чтобы избежать их: не нужно быть юридическим языком, чтобы иметь смысл кода и hellip;

+0

AFAICS 'i = ++ i' был хорошо определен в C++ 03, как правильно показано принятый ответ в связанном Q/A. – Columbo

+1

@Columbo: Если бы вы были так любезны обобщить или процитировать, я постараюсь опровергнуть иска, которое вы видели. Я не могу его найти, извините. Однако, так как это было очень просто в C++ 03, просто абсолютно никогда не изменяйте дважды или больше между точками последовательности, может быть, вам не нужно больше этого? C++ 03 §5/4 в [expr]: «Между предыдущими и следующей точкой последовательности скалярный объект должен иметь значение, которое его хранимое значение изменялось не более одного раза путем оценки выражения». –

+0

По-видимому, я неправильно понял понятие последовательности цели полностью. Я думал, что параграфы, приведенные в этом [ответе] (http://stackoverflow.com/a/17403467/3647361), достаточны, чтобы доказать четкость этого выражения. Но нигде ничего не говорится о точках последовательности (я думал, что их существование подразумевается *, но это кажется неправильным в ретроспективе). На самом деле, кажется, что, поскольку они происходят так редко, они определяют много концептуально тонкого кода для вызова UB. К счастью, это было исправлено на C++ 11. – Columbo