Инициализация агрегатного члена в 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 — повторяющиеся случаи) относительно того, где берется начальное значение. от (или нет), :

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 и стандарт C++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 еще раз). Почему инициализаторы элементов по умолчанию также не игнорируются?

На самом деле, читая стандартную цитату С++ 14, я понял, что случай 010 будет равен 12 (это ноль), а случаи 011 и 001 будут равны нулю (как они есть на самом деле). Так что я не понимаю, почему a.b2 иногда считается "имеющим инициализатор", а иногда - нет.


person J L    schedule 10.01.2017    source источник
comment
{ 1 } заменяет { 11, 12, 13 }. Внутренние инициализаторы по умолчанию по-прежнему применяются, если их не переопределяет список инициализации (что происходит с b2.a, но не с b2.c и b2.e).   -  person Baum mit Augen    schedule 10.01.2017


Ответы (2)


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

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

В определении B говорится, что 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 } в вашем примере, как о полностью сконструированных объектах B, задолго до того, как конструктор A их увидит.

person Toby Speight    schedule 10.01.2017
comment
Спасибо @Тоби. Однако возник другой вопрос. Если я объявлю A a;, инициализируется ли a.x? Я проверил, протестировал и понял все остальные случаи (A a = {}; A a ={{}}; и т. д.), но нашел разные ответы и источники для A a;. Предполагая, что struct A {} определено в глобальном пространстве имен, это должен быть статический объект хранения, поэтому он должен быть инициализирован (Стандарт С++ 14 3.6.2); в противном случае при автоматическом хранении это неопределенное значение. Однако Clang выводит случайное значение (GCC инициализирует a.x нулем). Если определение только struct A { int x;};, GCC говорит, что оно не определено, и Clang выводит 0. - person J L; 11.01.2017
comment
@JL, я думаю A a; оставляет a.x неопределенным (в отличие от, как вы говорите, A a = {};), но не просто верьте мне в этом - задайте другой вопрос, если на него еще нет ответа где-то. Кстати, неопределенное значение может быть 0... - person Toby Speight; 11.01.2017

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

person wally    schedule 10.01.2017