Являются ли множественные мутации одной и той же переменной в списках инициализации undefined поведение pre С++ 11

Рассмотрим следующий код:

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

    return 0 ;
}

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

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

Я не выступаю за такой код, но аналогичный код возник в другом вопросе, и возникли разногласия по поводу того, определено оно или нет в соответствии со стандартным pre-С++ 11. В С++ 11 это поведение является четко определенным поведением в соответствии с Являются ли множественные мутации в списках инициализатора undefined, и действительно, если я использую -std=c++11, тогда предупреждение уходит.

Если мы посмотрим на pre-С++ 11 черновик проекта, у него нет того же языка, который содержит список инициализаторов, поэтому кажется мы остаемся с Chapter 5 выражением абзаца 4, в котором говорится:

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

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

Ответ 1

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

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

и в пункте 15 говорится:

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

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

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

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

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

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

который заканчивается как отчет о дефекте 430, который я укажу:

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