Когда внешние фигурные скобки опущены в списке инициализаторов?

У меня есть ошибка C2078 в VC2010 при компиляции кода ниже.

struct A
  {
  int foo;
  double bar;
  };

std::array<A, 2> a1 = 
  // error C2078: too many initializers
  {
    {0, 0.1},
    {2, 3.4}
  };

// OK
std::array<double, 2> a2 = {0.1, 2.3};

Я выяснил, что правильный синтаксис для a1 -

std::array<A, 2> a1 = 
  {{
    {0, 0.1},
    {2, 3.4}
  }};

Вопрос: зачем нужны дополнительные фигурные скобки для a1, но не обязательные для a2?

Обновление

Вопрос, похоже, не определен для std:: array. Некоторые примеры:

struct B
  {
  int foo[2];
  };

// OK
B meow1 = {1,2};
B bark1 = {{1,2}};

struct C
  {
  struct 
    { 
    int a, b; 
    } foo;
  };

// OK
C meow2 = {1,2};
C bark2 = {{1,2}};

struct D
  {
  struct 
    { 
    int a, b; 
    } foo[2];
  };

D meow3 = {{1,2},{3,4}};  // error C2078: too many initializers
D bark3 = {{{1,2},{3,4}}};

Я все еще не понимаю, почему struct D дает ошибку, но B и C не делают.

Ответ 1

Необходимы дополнительные фигурные скобки, поскольку std::array - это совокупность и POD, в отличие от других контейнеров в стандартной библиотеке. std::array не имеет определяемого пользователем конструктора. Его первым элементом данных является массив размером N (который вы передаете как аргумент шаблона), и этот элемент инициализируется инициализатором. Дополнительные привязки необходимы для внутреннего массива, который непосредственно инициализируется.

Ситуация такая же, как:

//define this aggregate - no user-defined constructor
struct Aarray
{
   A data[2];  //data is an internal array
};

Как бы вы инициализировали это? Если вы это сделаете:

Aarray a1 =
{
   {0, 0.1},
   {2, 3.4}
};

он дает ошибку компиляции:

ошибка: слишком много инициализаторов для "Aarray"

Это та же ошибка, которую вы получаете в случае std::array (если вы используете GCC).

Итак, правильная вещь - использовать фигурные скобки следующим образом:

Aarray a1 =
{
  {  //<--this tells the compiler that initialization of `data` starts

        { //<-- initialization of `data[0]` starts

           0, 0.1

        }, //<-- initialization of `data[0]` ends

       {2, 3.4}  //initialization of data[1] starts and ends, as above

  } //<--this tells the compiler that initialization of `data` ends
};

который компилируется отлично. Еще раз, дополнительные скобки необходимы, потому что вы инициализируете внутренний массив.

-

Теперь возникает вопрос, почему дополнительные привязки не нужны в случае double?

Это потому, что double не является агрегатом, а A -. Другими словами, std::array<double, 2> представляет собой совокупность агрегата, а std::array<A, 2> - совокупность совокупности агрегата 1.

1. Я думаю, что дополнительные привязки по-прежнему необходимы и в случае двойного (например, this), чтобы полностью соответствовать стандарту, но код работает без них. Кажется, мне нужно снова проскочить в спецификации!. Забастовкa >

Подробнее о фигурных скобках и дополнительных фигурных скобках

Я вырыл спецификацию. Этот раздел (§8.5.1/11 из С++ 11) интересен и применим к этому случаю:

В объявлении формы

T x = { a };

скобки можно удалить в списке инициализаторов следующим образом. Если список инициализаторов начинается с левой фигурной скобки, то последующий разделенный запятыми список инициализатор-предложений инициализирует элементы субагрегата; ошибочно, чтобы было больше предложений инициализатора, чем членов. Если, однако, список инициализаторов для суммирования не начинается с левой скобки, тогда для инициализации членов субагрегата принимаются только достаточные инициализационные предложения из списка; любые оставшиеся условия инициализации оставляются для инициализации следующего члена агрегата, членом которого является текущий субагрегат. [Пример:

float y[4][3] = {
{ 1, 3, 5 },
{ 2, 4, 6 },
{ 3, 5, 7 },
};

является полностью фиксированной инициализацией: 1, 3 и 5 инициализируют первую строку массива y[0], а именно y[0][0], y[0][1] и y[0][2]. Аналогично, следующие две строки инициализируют y[1] и y[2]. Инициализатор заканчивается раньше, и поэтому элементы y[3]s инициализируются так, как если бы они были явно инициализированы выражением формы float(), то есть инициализированы с помощью 0.0. В следующем примере скобки в списке инициализаторов устранены; однако список инициализаторов имеет тот же эффект, что и полностью скопированный список инициализаторов вышеприведенного примера,

float y[4][3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7
};

Инициализатор для y начинается с левой скобки, но для y[0] нет, поэтому используются три элемента из списка. Аналогично, следующие три последовательно выполняются для y[1] и y[2]. -end пример]

Основываясь на том, что я понял из приведенной выше цитаты, могу сказать, что должно быть разрешено следующее:

//OKAY. Braces are completely elided for the inner-aggregate
std::array<A, 2> X =   
{
  0, 0.1,
  2, 3.4
};

//OKAY. Completely-braced initialization
std::array<A, 2> Y = 
{{
   {0, 0.1},
   {2, 3.4}
}};

В первом, скобки для внутреннего агрегата полностью исключены, а вторая имеет полностью фиксированную инициализацию. В вашем случае (в случае double) инициализация использует первый подход (скобки полностью устранены для внутреннего агрегата).

Но это должно быть запрещено:

//ILL-FORMED : neither braces-elided, nor fully-braced
std::array<A, 2> Z = 
{
  {0, 0.1},
  {2, 3.4}
};

Он не имеет ни фигур, ни скобок, и нет достаточного количества фигурных скобок для полной фиксации. Поэтому он плохо сформирован.