Увеличение переменной, используемой дважды в списке инициализаторов - поведение 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.