2017-01-10 3 views
3

Имея эту структуру:Совокупные член инициализации в C++ 14

struct A { 
    struct B { 
     int a = 21; 
     int b; 
     int c = 22; 
     int d; 
     int e = 23; 
    }; 
    B b1 = { 11, 12 }; 
    B b2 = { 11, 12, 13 }; 
    int x; 
}; 

И объявляя:

A a = { { 1, 2, 3, 4 }, { 1 }, 5 }; 

Согласно и Clang (3.8.0) и GCC (5.4.0), эти является значение 8 возможных комбинаций (a.b1.e и a.b2 повторяются случаи), в отношении которых начального значения берутся из (или нет),:

a.b1.a = 1 // 111 
a.b1.b = 2 // 110 
a.b1.c = 3 // 101 
a.b1.d = 4 // 100 
a.b2.b = 0 // 010 // Why not 12 instead of 0? -> Explained in N3605 
a.b2.c = 22 // 011 // Why not 0 instead of 22 ? Why not 13 ? 
a.b2.d = 0 // 000 
a.b2.e = 23 // 001 // Why not 0 instead of 23 ? 

Принимая во внимание примера, в N3605 и С ++ 14 Стандарта (ISO/IEC 14882: 2014), раздел 8.5.1, пункт 7:

Если есть меньше инициализатор-положение в списке чем есть члены в совокупности, то каждый член, явно не инициализированный, должен быть инициализирован из своего логического элемента с выравниванием или равным или, если нет нулевого логического элемента, из пустого списка инициализаторов (8.5.4).

Я предполагаю, что случай 010 правильный. Тогда почему случаи 011 (a.b2.c) и 001 (a.b2.e) также не равны нулю? Случай 010 равен нулю, потому что a.b2 "имеет инициализатор", поэтому "инициализатор нестатических данных" игнорируется "(N3605). Почему инициализаторы элемента по умолчанию не игнорируются?

Фактически, прочитав стандартную цитату C++ 14, мне было бы более разумно, что случай 010 будет равен 12 (это нуль), а случаи 011 и 001 будут равны нулю (как они есть на самом деле). Поэтому я не понимаю, почему a.b2 иногда считается «иметь инициализатор», а в других случаях это не так.

+2

«{1}' »заменяет« {11, 12, 13} ». Внутренние инициализаторы по умолчанию все еще применяются, если только список init не отменяет их (что происходит с 'b2.a', но не' b2.c' и 'b2.e'). –

ответ

6

Вы объявляете a с инициативами для всех своих членов: b1, b2 и x. Это означает, что мы строим, как будто

a.b1 = B{ 1, 2, 3, 4 }; 
a.b2 = B{ 1 }; 
a.x = 5; 

B «s определение говорит, что B{ 1, 2, 3, 4 } означает B{ 1, 2, 3, 4, 23 } и B{ 1 } означает B{ 1, 0, 22, 0, 23 }. И это именно то, что вы получите.


Если бы вы написали

A a = { { 1, 2, 3, 4 }, }; 

затем a.b2 бы инициализирована его значение по умолчанию {11, 12}:

a.b1 = B{ 1, 2, 3, 4 }; 
a.b2 = B{ 11, 12 }; 
a.x = {}; 

Это может помочь думать о те фигурные скобки, такие как { 1 } и { 11, 12 } в вашем примере как полностью c onstructed B объектов, задолго до того, как конструктор A даже видит их.

+0

Спасибо @Toby. Однако возник еще один вопрос. Если я объявляю «A a», инициализируется ли a.x? Я проверил, протестировал и понял все остальные случаи ('A a = {}; A a = {{}};' и т. Д.), Но я нашел разные ответы и источники для 'A a;'. Предполагая, что 'struct A {}' определяется в глобальном пространстве имен, он должен быть статическим объектом хранения, поэтому он должен быть инициализирован (C++ 14 Standard 3.6.2), в противном случае автоматическое хранение - неопределенное значение. Тем не менее, Clang выводит случайное значение (GCC инициализирует a.x до нуля). Если определение - это только 'struct A {int x;};' GCC говорит, что он не определен и Clang выводит 0. –

+1

@JL, I * think * 'A a;' оставляет 'ax' неопределенным (в отличие, как вы говорите,' ' A a = {}; '), но не просто доверяйте мне об этом - задайте другой вопрос, если он уже не ответил. BTW, неопределенное значение * может * быть 0 ... –

2

В этом примере {1, 2, 3, 4} является инициализатором для B b1. У компилятора уже есть инициализатор, так что теперь он больше не будет смотреть на { 11, 12 }.