Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a 2D std::array be initialized with two layers of list-initializers?

can someone help me understand why my compiler can't/doesn't deduce this? (using g++ 7.3)

Does not work:

#include <array>
std::array<std::array<double,2>,2> f() {
 return {{0,0},{0,0}};
}

Works fine:

#include <array>
std::array<std::array<double,2>,2> f() {
 return {std::array<double,2>{0,0},{0,0}};
}

Also weirdly this fails too:

#include <array>
std::array<std::array<double,2>,2> f() {
 return std::array<std::array<double,2>,2>{{0,0},{0,0}};
}

@1201ProgramAlarm pointed out that adding another set of curly braces works:

#include <array>
std::array<std::array<double,2>,2> f() {
 return {{{0,0},{0,0}}};
}

It's using aggregate initialization, because std::array has no constructor for brace-init-list. That's fine, but then why/how does this work?

std::array<double,2> x{1,2};

why does it handle this case but not the nested case?

like image 209
Peter Mitrano Avatar asked Sep 08 '18 02:09

Peter Mitrano


1 Answers

The container std::array is equivalently a struct holding a C-array (an implementation may not implement std::array in this way, but it should guarantee the semantic is the same), so it should be initialized by two layers of braces, i.e.

#include <array>
std::array<std::array<double,2>,2> f() {
   return {{{{0,0}},{{0,0}}}};
} 

Of course, braces in an initializer-list can be elided like what we usually do for a 2D array:

int arr[2][2] = {0,1,2,3};

... but the initializer-list that begins with the elided braces before the elision should not begin with a left brace after the elision. In other words, if an initializer-list begins with a left brace, the compiler will not consider the possibility that this initializer-list has elided outermost braces.

In your initializer {{0,0},{0,0}}, the sub-initializer {0,0},{0,0} begins with a left brace, so it is used to initialize the C-array itself. However, there are two clauses in the list while there is only one C-array, an error occurs.

In your initializer {std::array<double,2>{0,0},{0,0}}, the sub-initializer std::array<double,2>{0,0},{0,0} does not begin with a left brace, so it can be used to initialize the elements of the C-array, which is OK (recursively, {0,0} is OK to initialize an std::array<double,2> because the sub-initializer 0,0 does not begin with a left brace).


A suggestion: with this elision rule of braces, you can elide all inner braces, just like what we usually do for a 2D array:

#include <array>
std::array<std::array<double,2>,2> f() {
   return {0,0,0,0};
} 
like image 193
xskxzr Avatar answered Oct 17 '22 13:10

xskxzr