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

Рассмотрим следующий код, в котором мы инициализируем часть D основанный на другой части D:

struct c {
c() : D{rand(), D[0]} {}
int D[2];
};

int main() {
c C;
assert(C.D[0] == C.D[1]);
}

Является ли вышеуказанная программа четко определенной? Можем ли мы безопасно использовать одну часть того же массива для инициализации другой его части?

22

Решение

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

Да.

struct c {
int a[3];
c() : a{4, a[0], 3} {} // a[0] is initialized to 4.
// a[1] is initialized to whatever a[0] is. (4)
// a[2] is initialized to 3.
};

Но рассмотрим этот пример:

struct c {
int a[3];
c() : a{a[1], 4, a[1]} {} // a[0] is initialized to whatever a[1] is.(Garbage value)
// a[1] is initialized to 4.
// a[2] is initialized to what a[1] is now (4).
};

Здесь первый элемент в a будет какое-либо значение в a[1],
что, скорее всего, будет мусором.
Второй элемент инициализируется 4 и третий элемент инициализируется
к тому, что сейчас в a[1], которая является значением 4,

Кроме того, когда вы не перечислите все элементы в массиве внутри {},
элементы, которые не перечислены, будут инициализированы по умолчанию:

struct c {
int a[5]; // notice the size
c() : a{a[1], 2, 3, 4}{}  // a[0] will get value that is in a[1]
// but since a[1] has garbage value,
// it will be default initialized to 0.
// a[1] = 2
// a[2] = 3
// a[3] = 4
// a[4] is not listed and will get 0.
};

Однако перечисление уже инициализированного элемента даст вам желаемое значение.
Используя приведенный выше пример:

struct c {
int a[5];
c() : a{1, a[0], 3, 4}{}  // a[0] = 1
// a[1] = 1
// a[2] = 3
// a[3] = 4
// a[4] is not listed and will get 0.
};
11

Другие решения

Когда агрегаты (включая массивы) инициализируются из фигурного списка, каждый элемент агрегата инициализируется из соответствующего элемента списка («в порядке возрастания индекса или порядка элементов»). Несмотря на то, что я не могу найти точное правило, которое говорит, что инициализация каждого элемента выполняется после предыдущей, в Стандарте есть пример, который явно подразумевает, что это подразумеваемое значение. Пример находится в [dcl.init.aggr]:

struct S { int a; const char* b; int c; int d = b[a]; };
S ss = { 1, "asdf" };

инициализирует ss.a с 1, ss.b с "asdf", ss.c со значением выражения формы int{} (то есть, 0), а также ss.d со значением ss.b[ss.a] (то есть, ’s’)

16

В соответствии с cppreference.com:

Эффекты инициализации агрегата:

Каждый элемент массива или нестатический член класса, в порядке массива
индекс / внешний вид в определении класса, инициализируется копией из
соответствующий пункт списка инициализаторов.

Ваш код кажется в порядке.
Однако как-то сбивает с толку.

2

Это не очень хорошая практика, чтобы писать D{rand(),D[0]}потому что когда
конструктор будет запускаться не обязательно, что первый rand () будет
затем выполняется D [0], все зависит от компилятора, D [0] может быть
выполняется первым, в этом случае d [1] будет содержать значение мусора. Это
полностью зависит от компилятора, он может скомпилировать второй аргумент
сначала, а затем первый аргумент или наоборот, выполняя это
утверждение может привести к неизвестному поведению.

0