I've got error C2078 in VC2010 when compiling the code below.
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};
I found out that the correct syntax for a1
is
std::array<A, 2> a1 = {{ {0, 0.1}, {2, 3.4} }};
The question is: why extra braces are required for a1
but not required for a2
?
Update
The question seems to be not specific to std::array. Some examples:
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}}};
I still don't see why struct D
gives the error but B and C don't.
The extra braces are needed because std::array
is an aggregate and POD, unlike other containers in the standard library. std::array
doesn't have a user-defined constructor. Its first data member is an array of size N
(which you pass as a template argument), and this member is directly initialized with an initializer. The extra braces are needed for the internal array which is being directly initialized.
The situation is same as:
//define this aggregate - no user-defined constructor struct Aarray { A data[2]; //data is an internal array };
How would you initialize this? If you do this:
Aarray a1 = { {0, 0.1}, {2, 3.4} };
it gives a compilation error:
error: too many initializers for 'Aarray'
This is the same error which you get in the case of a std::array
(if you use GCC).
So the correct thing to do is to use braces as follows:
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 };
which compiles fine. Once again, the extra braces are needed because you're initializing the internal array.
--
Now the question is why are extra braces not needed in case of double
?
It is because double
is not an aggregate, while A
is. In other words, std::array<double, 2>
is an aggregate of aggregate, while std::array<A, 2>
is an aggregate of aggregate of aggregate1.
1. I think that extra braces are still needed in the case of double also (like this), to be completely conformant to the Standard, but the code works without them. It seems I need to dig through the spec again!.
I dug through the spec. This section (§8.5.1/11 from C++11) is interesting and applies to this case:
In a declaration of the form
T x = { a };
braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the members of a subaggregate; it is erroneous for there to be more initializer-clauses than members. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the members of the subaggregate; any remaining initializer-clauses are left to initialize the next member of the aggregate of which the current subaggregate is a member. [ Example:
float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, };
is a completely-braced initialization: 1, 3, and 5 initialize the first row of the array
y[0]
, namelyy[0][0]
,y[0][1]
, andy[0][2]
. Likewise the next two lines initializey[1]
andy[2]
. The initializer ends early and thereforey[3]s
elements are initialized as if explicitly initialized with an expression of the form float(), that is, are initialized with 0.0. In the following example, braces in the initializer-list are elided; however the initializer-list has the same effect as the completely-braced initializer-list of the above example,
float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
The initializer for y begins with a left brace, but the one for
y[0]
does not, therefore three elements from the list are used. Likewise the next three are taken successively fory[1]
andy[2]
. —end example ]
Based on what I understood from the above quote, I can say that the following should be allowed:
//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} }};
In the first one, braces for the inner-aggregate are completely elided, while the second has fully-braced initialization. In your case (the case of double
), the initialization uses the first approach (braces are completely elided for the inner aggregate).
But this should be disallowed:
//ILL-FORMED : neither braces-elided, nor fully-braced std::array<A, 2> Z = { {0, 0.1}, {2, 3.4} };
It is neither braces-elided, nor are there enough braces to be completely-braced initialization. Therefore, it is ill-formed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With