Могу ли я ссылаться на предыдущие члены списка инициализаторов?

Скажем, я хочу обратиться к члену initializer_list который я уже определил. Могу ли я это сделать?

Этот код компилирует и дает ожидаемое: "13 55" в Visual Studio и gcc, я просто хотел бы знать, что это законно:

const int foo[2] = {13, foo[0] + 42};

Ответ 1

Таким образом, мы имеем здесь общую инициализацию, описанную в разделе 8.5.1 проекта C++, и в нем говорится:

Агрегатом является массив или класс [...]

а также:

Когда агрегат инициализируется списком инициализаторов, как указано в 8.5.4, элементы списка инициализаторов берутся как инициализаторы для членов агрегата, увеличивая индекс или порядок членов. Каждый элемент инициализируется копией из соответствующего параметра initializer-[[...]

Хотя кажется разумным, что побочные эффекты от инициализации каждого члена агрегата должны быть секвенированы до следующего, поскольку каждый элемент в списке инициализаторов является полным выражением. Стандарт на самом деле не гарантирует этого, мы можем видеть это из отчета об ошибках 1343, который гласит:

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

а также отмечает:

Агрегатная инициализация также может включать более одного полного выражения, поэтому ограничение выше на "инициализацию объекта, отличного от класса", неверно.

и мы можем видеть из связанного стд-дискуссионного раздела Ричард Смит говорит:

[intro.execution] p10: "Полное выражение - это выражение, которое не является подвыражением другого выражения. [...] Если языковая конструкция определена для получения неявного вызова функции, использование языковой конструкции считается выражением для целей этого определения ".

Поскольку бит-init-list не является выражением, и в этом случае это не приводит к вызову функции, 5 и si являются отдельными полными выражениями. Затем:

[intro.execution] p14: "Каждое вычисление значения и побочный эффект, связанный с полным выражением, секвенируются перед вычислением каждого значения и побочным эффектом, связанным со следующим полным выражением, которое должно быть оценено".

Таким образом, единственный вопрос заключается в побочном эффекте инициализации si, "связанного с" оценкой полного выражения "5"? Я думаю, что единственное разумное предположение состоит в том, что: если 5 инициализировали член типа класса, вызов конструктора, очевидно, был бы частью полного выражения по определению в [intro.execution] p10, поэтому естественно предположить что то же самое верно для скалярных типов.

Тем не менее, я не думаю, что стандарт действительно прямо говорит об этом.

Таким образом, в настоящее время этот стандарт не указан стандартом и на него нельзя положиться, хотя я был бы удивлен, если бы реализация не рассматривала его так, как вы ожидаете.

Для простого случая вроде этого нечто похожее на это кажется лучшей альтернативой:

constexpr int value = 13 ;
const int foo[2] = {value, value+42};

Изменения в C++ 17

Предложение P0507R0: Основной вопрос 1343: Последовательность инициализации неклассов уточняет точку полного выражения, поднятую здесь, но не отвечает на вопрос о том, включен ли побочный эффект инициализации в оценку полного выражения. Так что это не изменится, что это не указано.

Соответствующие изменения для этого вопроса находятся в [intro.execution]:

Составляющее выражение определяется следующим образом:

(9.1). Составным выражением выражения является это выражение.

(9.2). Составные выражения списка с привязкой-инициализацией или (возможно, заключенного в скобки) списка выражений являются составными выражениями элементов соответствующего списка.

(9.3) . Составные выражения элемента с равным-равным-инициализатором формы = initializer-clause являются составными выражениями условия initializer. [ Пример:

struct A { int x; };
struct B { int y; struct A a; };
B b = { 5, { 1+1 } };

Составляющие выражения инициализатора, используемые для инициализации b, равны 5 и 1 + 1. -End пример]

и [intro.execution] p12:

Полное выражение

(12.1) - неоцененный операнд (п. 8),

(12.2) - постоянное выражение (8.20),

(12.3) - init-declarator (раздел 11) или mem-initializer (15.6.2), включая составляющие выражения инициализатора,

(12.4) - вызов деструктора, сгенерированного в конце срока жизни объекта, отличного от временного объекта (15.2), или

(12.5) - выражение, которое не является подвыражением другого выражения и которое не является частью полноразмерного выражения.

Таким образом, в этом случае как 13 и foo[0] + 42 являются составными выражениями, которые являются частью полного выражения. Это разрыв с анализом здесь, который полагал, что каждый из них будет их собственным полным выражением.

Изменения в C++ 20

Предложение назначенного инициализации: P0329 содержит следующее дополнение, которое, как представляется, делает это четко определенным:

Добавить новый абзац в 11.6.1 [dcl.init.aggr]:

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

Мы видим, что это отражено в последнем проекте стандарта.