When are outer braces omitted from the initializer list?

I have 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} }}; 

Question: why do we need additional braces for a1 , but not required for a2 ?

Update

The question does not seem to be defined for 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 understand why struct D gives an error, but B and C do not.

+42
c ++ c ++ 11 visual-c ++ - 2010
Jul 31 2018-12-12T00:
source share
1 answer

Additional curly braces are needed, since std::array is a collection and POD, unlike other containers in the standard library. std::array does not have a user-defined constructor. Its first data element is an array of size N (which you pass as a template argument), and this element is initialized with an initializer. Additional bindings are needed for the internal array, which is directly initialized.

The situation is the 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 you get in case of std::array (if you use GCC).

So the right thing is to use curly 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, extra brackets are needed because you are initializing the internal array.

-

Now the question is, why are additional bindings not necessary in the case of double ?

This is because double not an aggregate, but A is. In other words, std::array<double, 2> is an aggregate aggregate, and std::array<A, 2> is an aggregate aggregate aggregate 1 .

1. I think that additional bindings are still necessary in the case of a double one (for example, this ) to fully comply with the standard, but the code works without them. It seems I need to slip through the spec again! . Strike>

Learn more about braces and optional braces.

I dug a spec. This section (ยง8.5.1 / 11 of C ++ 11) is interesting and applicable to this case:

In the form declaration

 T x = { a }; 

brackets can be removed in the initializer list as follows . If the list of initializers begins with the left curly bracket, then the subsequent comma-separated list of initializer-sentences initializes the elements of the sub-aggregate; it is a mistake to have more initializer proposals than members. If, however, the list of initializers for summing does not start with the left bracket, then only sufficient initialization proposals from the list are accepted for initialization of the members of the subaggregate; any remaining initialization conditions are left to initialize the next unit member of which the current sub-unit is a member. [Example:

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

is a completely fixed initialization: 1, 3, and 5 initialize the first line of the array y[0] , namely y[0][0] , y[0][1] and y[0][2] . Similarly, the next two lines initialize y[1] and y[2] . The initializer ends earlier, and therefore the elements y[3]s initialized as if they were explicitly initialized by an expression of the form float (), that is, initialized with 0.0. In the following example, the brackets in the initializer list are removed; however, the initializer list has the same effect as the fully copied initializer list of the above example,

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

The initializer for y starts with the left bracket, but for y[0] not, therefore, three elements from the list are used. Similarly, the following three are executed sequentially for y[1] and y[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, the brackets for the indoor unit are completely excluded, and the second has a completely fixed initialization. In your case (in the case of double ) initialization uses the first approach (the brackets are completely removed for the internal aggregate).

But this should be prohibited:

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

It has no shapes or brackets, and there are not enough braces for full fixation. Therefore, it is poorly formed.

+55
Jul 31 '12 at 7:18
source share



All Articles