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

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

int i = 0; struct S { S(...) {} operator int() { return i; } }; int main() { i = S{++i, ++i}; } 

Любые ответы принимаются.

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

Цитируя § 8.5.4 стандарта C ++:

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

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

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

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

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

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

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

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

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

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

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

РЕДАКТИРОВАНИЕ

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

  • Передает ли объект C ++ в свой собственный конструктор законным?
  • Ошибка компиляции C ++?
  • Сопоставлено ли целочисленное переполнение по-прежнему неопределенным поведением в C ++?
  • Можем ли мы изменить значение объекта, определенного с помощью const через указатели?
  • Почему неподписанное целочисленное переполнение определенного поведения, но недопустимое целочисленное число целых чисел?
  • Почему целочисленное переполнение на x86 с GCC вызывает бесконечный цикл?
  • Виртуальный деструктор и неопределенное поведение
  • Как спаривание нового с удалением может привести только к утечке памяти?
  • Неопределенное поведение и точки последовательности перезагружены
  • Почему char * вызывает неопределенное поведение, а char - нет?
  • Реализация C ++, которая обнаруживает неопределенное поведение?
  • Давайте будем гением компьютера.