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 Plain Old Datatype (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!.
#More on braces and extra braces
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.