2015-06-27 6 views
22

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

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

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

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

+5

Моя первая мысль - «не делать». Моя вторая мысль - «инициализатор списков havr ordering gurantees». Моя третья мысль: «инициализация происходит после оценки аргумента, нет?» – Yakk

ответ

11

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

Да.

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. 
}; 
16

Когда агрегаты (включая массивы) инициализируются из скованного списка, каждый элемент агрегата инициализируется из соответствующего элемента списка («в возрастающем индексе или порядке члена»). Несмотря на то, что я не могу найти точное правило, в котором говорится, что инициализация каждого элемента секвенирована после предыдущего, в стандарте есть пример, который ясно подразумевает, что это предназначение. Пример в [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’)

+3

[dcl.init.list]/4: «В списке инициализаторов списка с привязкой-инициализацией предложения инициализатора, включая все, что получается из разложений пакетов (14.5.3), оцениваются в порядке, указанном в которые они кажутся, то есть вычисление каждого значения и побочный эффект , связанный с заданным предложением инициализатора, секвенируются перед каждым вычислением значения и боковым эффектом , связанным с любым предложением инициализатора, которое следует за ним в разделенном запятыми списке инициализатора- список." – Casey

+2

@Casey: Влияют ли побочные эффекты на инициализацию агрегатного элемента? –

+0

Инициализация, безусловно, является побочным эффектом, и небезосновательно утверждать, что побочный эффект связан с данным * инициализатором-предложением *. Учитывая отсутствие какого-либо другого языка, я думаю, что это предполагаемая интерпретация. – Casey

0

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

+3

Стандарт ясен, что 'rand()' вызывается перед 'D [0]', поэтому вы ошибаетесь: il's не являются вызовами с окончанием, где вы были бы правы. Менее ясно, инициализируется ли 'D [0]' перед чтением 'D [0]'. – Yakk

+1

В настоящее время я изучаю C++, я прочитал это поведение в C++-праймере, в книге, которую я использую, в книге было четко указано, что какой аргумент выполняется первым, - это неизвестное поведение, и не является хорошей практикой сделать аргумент зависеть по другому аргументу для инициализации –

+3

Это цитата откуда-то, или почему вы положили ее в блок-образец? Если это так, пожалуйста, укажите его правильно (название, страница, автор, URL-адрес или так). – Bergi

2

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

Эффекты совокупной инициализации являются:

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

Ваш код кажется прекрасным. Как-то смущает.

+0

Почему это запутывает? – emlai

+0

На мой взгляд, это уменьшает читаемость, и многие люди не знают, что они будут оцениваться последовательно или нет. – deepmax