4
C++

Рассмотрим следующий код:ли множественные мутации одного и того же переменной в списках инициализаторов неопределенное поведение предварительно 11

int main() 
{ 
    int count = 0 ; 
    int arrInt[2] = { count++, count++ } ; 

    return 0 ; 
} 

Если скомпилировать код, используя clang -std=c++03 производит следующее предупреждение (live example):

warning: multiple unsequenced modifications to 'count' [-Wunsequenced] 
    int arrInt[2] = { count++, count++ } ; 
         ^  ~~ 

Я не защищаю для такого кода, но подобный код подошел другой вопрос и есть разногласия по поводу того, оно определено или нет в соответствии со стандартом pre-C++ 11. В C++ 11 это поведение хорошо определено в соответствии с Are multiple mutations within initializer lists undefined behavior, и действительно, если я использую -std=c++11, тогда предупреждение уходит.

Если мы посмотрим на пред- 11draft standardC++ это не один и тот же язык, охватывающий инициализатора-лист так, кажется, мы остались с Chapter 5Выражения пункта , который говорит:

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

Для того, чтобы это было неопределенные, казалось бы, мы бы интерпретировать count++, count++ как выражение и поэтому каждый count++ как подвыражения, поэтому этот код неопределенный предварительно C++ 11?

ответ

4

Код не не определено пре- C++ 11 но порядок оценки является не определено. Если мы посмотрим на проект стандарта разделе 1.9 исполнение программы пункт говорит:

Полное выражение является выражением, которое не является подвыражением другого выражения. [...]

и пункт говорит:

Существует точка последовательности по завершении оценки каждого полного выражения 12).

тогда вопрос count++, count++ является ли полного выражения и каждое count++Подвыражения или каждый count++ это собственное полного выражения и, следовательно, существует точки последовательности после каждых из них? если мы посмотрим на грамматику для этой инициализации из раздела 8.5Инициализаторы:

initializer-clause: 
    assignment-expression 
    { initializer-list ,opt } 
    { } 
initializer-list: 
    initializer-clause 
    initializer-list , initializer-clause 

только выражение мы имеем это назначение выражение и , разделения компонентов является частью initializer- список, а не часть выражения , и поэтому каждый count++ является полным выражением и есть пункт последовательности после каждого.

Эта интерпретация подтверждается следующим gccbug report, который имеет очень похожий код на мой (я придумал мой пример пути, прежде чем я нашел этот отчет об ошибке):

int count = 23; 
int foo[] = { count++, count++, count++ }; 

, который заканчивается в качестве defect report 430, который я цитирую:

[...] Я считаю, что стандарт ясно, что каждое выражение инициализатор в выше, является полным выражением (1,9 [intro.execution]/12-13; см также вопрос 392), и поэтому после каждого выражения есть точка последовательности (1.9 [intro.execution]/16). Я согласен с тем, что стандарт, похоже, не диктует порядок, в котором выражения оцениваются, и, возможно, это должно быть. Кто-нибудь знает о компиляторе, который не оценил бы выражения слева направо?

+0

Интересно. Казалось бы, существенным моментом является то, что выражения в истинном списке инициализаторов считаются полными выражениями, потому что невозможно найти более крупную структуру с «выражением» в ее имени, которое их содержит. Это актуально, потому что синтаксически _initializer-list_ также используется для _expression-list_, что также встречается в вызовах функций для экземпляра. Там ** является ** охватывающим выражением (_postfix-expression_ для полного вызова функции), так что отдельные аргументы не считаются полными выражениями, и после них нет точек последовательности. –

+0

@MarcvanLeeuwen, это правильно, я всегда хотел добавить раздел, объясняющий, почему это отличается от вызова функции. Я рад, что вы смогли это понять, это всегда полезный опыт. –

Смежные вопросы