Являются ли множественные мутации в списке инициализаторов undefined?

Мне интересны списки инициализаторов и точки последовательности. Некоторое время назад я читал, что порядок оценки в списках инициализаторов оставлен вправо. Если это так, то между точками оценки должна быть какая-то точка последовательности, не так ли? Итак, с этим сказал следующий действующий код? Есть ли что-нибудь, что вызывает в нем поведение undefined?

int i = 0;

struct S {
    S(...) {} 
    operator int() { return i; }
};

int main() {
    i = S{++i, ++i};
}

Приветствуются все ответы.

Ответ 1

Да, код действителен и не имеет поведения undefined. Выражения в списке инициализаторов оцениваются слева направо и упорядочиваются перед оценкой конструктора S. Поэтому ваша программа должна последовательно присваивать значение 2 переменной i.

Цитата из § 8.5.4 стандарта С++:

"В списке инициализаторов списка с привязкой к инициализации предложения инициализатора, включая все, что является результатом разложений пакетов (14.5.3), оцениваются в том порядке, в котором они отображаются. То есть, каждое вычисление значения и побочный эффект, связанное с заданным предложением инициализатора , секвенируется до с каждым вычислением значения и побочным эффектом, связанным с любое предложение инициализатора, которое следует за ним в разделенном запятыми списке списка инициализаторов."

Таким образом, происходит следующее:

  • ++i получает оценку, давая i = 1 (первый аргумент конструктора S);
  • ++i получает оценку, давая i = 2 (второй аргумент конструктора S); Выполняется конструктор
  • S;
  • S выполняется оператор преобразования, возвращающее значение 2;
  • Значение 2 присваивается i (уже имеющему значение 2).

Другим важным пунктом Стандарта является § 1.9/15, в котором также упоминаются аналогичные примеры, которые имеют поведение undefined:

i = v[i++]; // the behavior is undefined
i = i++ + 1; // the behavior is undefined

Однако в том же абзаце говорится:

"За исключением тех случаев, когда отмечено, оценки операндов отдельных операторов и подвыражений отдельных выражений не имеют никакого значения. [...] При вызове функции (независимо от того, функция является встроенной), каждое вычисление значения и побочный эффект, связанный с любым выражением аргумента, или с выражением postfix, обозначающим вызываемую функцию, секвенируются перед выполнением каждого выражения или оператора в теле вызываемой функции."

Поскольку 1) оценка выражений в списке инициализаторов секвенирована слева направо, 2) выполнение конструктора S секвенируется после оценки всех выражений в списке инициализаторов и 3) присваивание i секвенируется после выполнения конструктора S (и его оператора преобразования), поведение корректно определено.

Ответ 2

Да, у вас есть случай поведения undefined.

Ниже приведены примеры ситуаций, вызывающих поведение undefined:

  • Переменная изменяется несколько раз в пределах одной точки последовательности. Как канонический пример, выражение я = я ++ часто цитируется там, где назначение переменной я и ее приращение выполняются на в то же время. Чтобы узнать больше об этом виде ошибок, прочитайте раздел "точки последовательности" .
  • Использование переменной перед ее инициализацией. undefined происходит поведение при попытке использовать переменную.
  • Распределение памяти с использованием нового оператора [] и последующего выпуска используя оператор удаления. Например: T * p = new T [10]; удалить p;. Правильный код: T * p = new T [10]; delete [] p;.

EDITED

Также ваш код S {++ i, ++ i}; не компилируется для VS2012. Может быть, вы имеете в виду S (++ i, ++ i);? Если вы используете "()", то присутствует undefined behavour. В другом случае ваш исходный код неверен.