When Can Outer Braces Be Omitted in an Initializer List

When can outer braces be omitted in an initializer list?

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!.

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], namely y[0][0], y[0][1], and y[0][2]. Likewise the next two lines initialize y[1] and y[2]. The initializer ends early and therefore y[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 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 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.

Why can not omit braces when initializing map?

Because the map is a non-aggregate, and contains non-aggregate elements (std::pair<key_type, mapped_type>), so it requires an initializer-list full of initializer-lists, one for each pair.

std::pair<int,int> p0{ 1,2 }; // single pair
std::map<int, int> m { { 1,2 } }; // map with one element
std::map<int, int> m { { 1,2 }, { 3,4} }; // map with two elements

Bear in mind that the rules for brace elision apply to aggregates, so they do not apply here.

Why do brace-enclosed initializer lists not work for std::array

Here is working solution - you need double braces for arrays.

int main()
{
const vector<Widget> vw = {
{"vw1", {1,2,3}},
{"vw2", {1,2,3}} };
const array<Widget,2> aw = {{
{"aw1", {1,2,3}},
{"aw2", {1,2,3}} }};
const vector<Object> vo = {
{"vo1", {1,2,3}},
{"vo2", {1,2,3}} };
const array<Object,2> ao = {{
{"ao1", {1,2,3}},
{"ao2", {1,2,3}} }};
return 0;
}

Why?

http://en.cppreference.com/w/cpp/container/array

std::array is a container that encapsulates fixed size arrays.
This container is an aggregate type with the same semantics as a struct holding a C-style array T[N] as its only non-static data member. Unlike a C-style array, it doesn't decay to T* automatically. As an aggregate type, it can be initialized with aggregate-initialization given at most N initializers that are convertible to T: std::array a = {1,2,3};.

Theoretical implementation (it is more complicated in reality)

template <typename T, size_t size> 
struct array
{
T data[size];
}

So first brace is to aggregate-initializate array object on its own, second brace to aggregate-initializate internal "legacy C style" array.

Can we omit the double-braces for std::array in C++14?

Actually you can write the following in C++11 also:

std::array<int, 3> arr{1,2,3};

It is completely valid syntax.

What is not allowed in C++11 though is something like this case (see that topic; I don't want to write this here again, it is a long post). So if you ask that then, yes, we can omit the extra braces in C++14. This is the proposal:

  • Uniform initialization for arrays and class aggregate types

  • The introduction says

    This document proposes a slight relaxation of the rules for eliding braces from aggregate initialization in order to make initialization of arrays and class aggregates more uniform. This change is required in order to support class aggregate types with a single member subaggregate that behave similarly to their array counterparts (i.e. std::array).

Hope that helps.

Initializer-list for initializing 2D std::array member

The rules for braces are very complicated when it comes to nested structures.

The simplest form in your code would be this:

Block<3, 2> b {1, 2, 3, 4, 5, 6};

That basically omits all the inner braces — these omissions are allowed by the language.

The next syntax, which is slightly complex, is this:

Block<3, 2> b {{1, 2, 3, 4, 5, 6}};

It still omits braces, but as far as Block and as its member is concerned it is FULLY braced. It omits braces for the array and its members.

And this one is FULLY braced:

Block<3, 2> b {{{ {{1, 2,3}}, {{4,5,6}} }}}; 

It braces for all inner structures.

All forms compiles fine.

See my other answer for detailed explanation:

  • When can outer braces be omitted in an initializer list?

initializing of a struct of whose members are array of another struct

The structure body is an aggregate that contains data members that in turn are aggregates.

You need to write

body r_plate = { { { 0,0 },{ 5,0 },{ 5,1 },{ 0,1 } } };

That is the structure body contains an array so you have to write

body r_plate = { { ... } };

and each element of the array is an object of the structure type. So you will have

body r_plate = { { { 0,0 },{ 5,0 },{ 5,1 },{ 0,1 } } };

The following initializations will be less readable but correct

body r_plate = { { 0,0,5,0,5,1,0,1 } };

and

body r_plate = { 0,0,5,0,5,1,0,1 };

Here is a demonstration program.

#include <iostream>

typedef struct coordinate{
double x;
double y;
}point;

typedef struct sc_cell{ // single cell
point sc[4];
}cell;

typedef struct sb_body { // for single body
point sb[4];
}body;

using namespace std;

int main()
{
body r_plate = { 0,0,5,0,5,1,0,1 };

for ( const auto &p : r_plate.sb )
{
std::cout << "( " << p.x << ", " << p.y << " ) ";
}
std::cout << '\n';

r_plate = { { 0,0,5,0,5,1,0,1 } };

for ( const auto &p : r_plate.sb )
{
std::cout << "( " << p.x << ", " << p.y << " ) ";
}
std::cout << '\n';

r_plate = { { { 0,0 }, { 5,0 } , { 5,1 }, { 0,1 } } };

for ( const auto &p : r_plate.sb )
{
std::cout << "( " << p.x << ", " << p.y << " ) ";
}
std::cout << '\n';

return 0;
}

The program output is

( 0, 0 ) ( 5, 0 ) ( 5, 1 ) ( 0, 1 ) 
( 0, 0 ) ( 5, 0 ) ( 5, 1 ) ( 0, 1 )
( 0, 0 ) ( 5, 0 ) ( 5, 1 ) ( 0, 1 )

As for this assignment

r_plate = { { 0,0 },{ 5,0 },{ 5,1 },{ 0,1 } };

then the first inner brace is considered as starting point of the list-initialization of the array. As the structure has only one data member (the array) then all other list-initializations apart the first one do not have corresponding data members of the structure. So the compiler issues an error.

Is it correct to initialize std::array by one pair of curly brackets if zeroed array needed?

According to the C++ Standard (8.5.1 Aggregates)

7 If there are fewer initializer-clauses in the list than there are
members in the aggregate, then each member not explicitly initialized
shall be initialized from its brace-or-equal-initializer or, if there
is no brace-or-equalinitializer, from an empty initializer list
(8.5.4).

and (8.5.4 List-initialization p.#3)

— Otherwise, if the initializer list has no elements, the object is
value-initialized.

Thus initializations

std::array<int, 10> array {};

and

std::array<int, 10> array { {  } };

are equivalent.

Why is the C++ initializer_list behavior for std::vector and std::array different?

std::array<T, N> is an aggregate: it doesn't have any user-declared constructors, not even one taking a std::initializer_list. Initialization using braces is performed using aggregate initialization, a feature of C++ that was inherited from C.

The "old style" of aggregate initialization uses the =:

std::array<int, 4> y = { { 1, 2, 3, 4 } };

With this old style of aggregate initialization, extra braces may be elided, so this is equivalent to:

std::array<int, 4> y = { 1, 2, 3, 4 };

However, these extra braces may only be elided "in a declaration of the form T x = { a };" (C++11 §8.5.1/11), that is, when the old style = is used . This rule allowing brace elision does not apply for direct list initialization. A footnote here reads: "Braces cannot be elided in other uses of list-initialization."

There is a defect report concerning this restriction: CWG defect #1270. If the proposed resolution is adopted, brace elision will be allowed for other forms of list initialization, and the following will be well-formed:

std::array<int, 4> y{ 1, 2, 3, 4 };

(Hat tip to Ville Voutilainen for finding the defect report.)



Related Topics



Leave a reply



Submit