Увеличение переменной, используемой дважды в списке инициализаторов - поведение undefined?

Изменить: не ответил - связанный вопрос касался обычных значений r, списки инициализаторов - это отдельная, если это связано концепция.

Является ли этот оператор корректным или использует префиксный оператор инкремента в списке инициализаторов для переменной, которая дважды появляется в списке, undefined поведение?

struct T t = { i, ++i };

Меня больше всего интересует ANSI C, но было бы полезно узнать, отличаются ли другие версии C и/или С++. И если аналогичные конструкции, подобные следующим, являются законными:

struct T t = { i, i++ };

struct T t = { ++i, ++i };

struct T t = { i++, ++i };

struct T t = { i++, i++ };

Ответы

Ответ 1

С

В C (не обязательно тот же ответ, что и для С++), нет никаких точек последовательности, связанных с компонентами списка инициализаторов.

Стандарт C11, ISO/IEC 9899: 2011, говорится в разделе §.6.7.9 Инициализация:

¶19 Инициализация должна выполняться в порядке списка инициализаторов, причем каждый инициализатор предоставил для конкретного подобъекта, переопределяя любой ранее указанный инициализатор для того же подобъекта; 151)

151) Любой инициализатор для подобъекта, который переопределен и поэтому не используется для инициализации этого подобъекта, может вообще не оцениваться.

Это звучит многообещающе, но...

¶23 Оценки выражений списка инициализации неопределенно секвенированы относительно друг друга и, следовательно, порядок, в котором происходят какие-либо побочные эффекты, не указан. 152)

152) В частности, порядок оценки не должен совпадать с порядком инициализации субобъекта.

Итак, (в C) порядок оценки неопределенно секвенирован, и вы не можете рассчитывать, когда происходят приращения (или, в крайних случаях, не проиллюстрированный кодом в вопросе, происходят ли приращения).

В C99 (ISO/IEC 9899: 1999) номер раздела - §6.7.8, но пункты 19 и 23 имеют по существу одно и то же содержимое, за исключением того, что номера сносок различны.

В C90 (ISO/IEC 9899: 1990) проблема не рассматривается явно.

С++

Судя по songyuanyao answer, правила на С++ 11 (и позже) отличных от таковых в C11. Подобные вещи подчеркивают, что языки C и С++ отличаются друг от друга и затрудняют составление исчерпывающих ответов на вопросы, помеченные обоими языками.

Близкие вопросы

Есть как минимум два других вопроса, связанных с побочными эффектами (например, ++) в контексте, отличном от инициализаторов. Оба они тоже должны быть прочитаны. Второй, в частности, представляет интерес для пользователей С++; первый помечен как C, а не С++, и поэтому имеет самое большое значение для тех, кто интересуется C.

Оба были отмечены πάντα ῥεῖ в комментариях.

Ответ 2

С++ 11 и более поздние версии

Поведение корректно определено для инициализации списка. В соответствии с секвенированными ранее правилами (начиная с С++ 11):

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

Итак, для struct T t = { i, ++i }; сначала будет оценен i, а затем ++i порядок будет определен корректно. И все остальные образцы тоже будут хороши.

Цитаты из стандарта С++, $8.6.4/4 Инициализация списка [Dcl.init.list]:

(акцент мой)

В списке инициализаторов списка с привязкой-инициализацией инициализатор-предложения, в том числе любые из результатов расширения пакета ([temp.variadic]), оцениваются в том порядке, в котором они отображаются. То есть вычисление каждого значения и побочный эффект, связанный с заданное условие инициализатора секвенируется перед вычислением каждого значения и побочный эффект, связанный с любым предложением инициализатора, которое следует за ним в разделенном запятыми списке списка инициализаторов. [Примечание: это порядок оценки выполняется независимо от семантики инициализация; например, оно применяется, когда элементы initializer-list интерпретируются как аргументы вызова конструктора, даже если обычно нет ограничений последовательности на аргументы вызова. - конечная нота]

Ответ 3

В C11 поведение всех этих инициализаций не является undefined. См. 6.7.9/23:

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

Термин неопределенно секвенированный определяется как таковой (5.1.2.3):

Оценки A и B неопределенно секвенированы, когда A секвенирован либо до, либо после B, но не указано, что.

В C99 не был четко сформулирован используемый язык, будь то та же ситуация или поведение undefined. В C89 проблема вообще не упоминается, поэтому мы, вероятно, должны предположить, что в C89 это undefined.