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

1

9

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?

Frederickson answered 8/9, 2018 at 2:49 Comment(4)
What about the size? you have not defined the size of outer array. That may be causing the problem.Lorrinelorry
You need another set of curly braces.Physostomous
By the way, why not use typedef double mat[2][2];?Ryter
Very close: #12844975Gantz
M
7

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};
} 
Monocycle answered 8/9, 2018 at 4:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.