Consider the following C++ code:
struct My_Struct
{
int a;
int b;
};
Now I want to declare a constant std::array of these structures:
Option A:
const std::array<My_Struct,2> my_array =
{
{1,2},
{2,3}
};
Option B:
const std::array<My_Struct,2> my_array =
{
My_Struct(1,2),
My_Struct(2,3)
};
Question: why does option A fail to compile and only option B compiles? What’s wrong with nested curly braces when initializing std::array?
It seems to me that compiler should have all necessary information to successfully compile option A, yet it produces "too many initializer values" error.
Meanwhile, nested curly braces work really good when initializing plain arrays:
const My_Struct my_array[] =
{
{1,2},
{2,3}
};
Compiler is MSVC 17.2, C++20 mode on.
I can live with option B, but I would like to learn what exactly am I failing to understand in option A in terms of C++ knowledge.
>Solution :
std::array is a class that contains an actual array. It looks something like this:
template <typename T, size_t N>
struct array
{
T _unspecified_name[N];
// Member functions.
};
Note that std::array has no constructors or private data members, so it is an aggregate.
When you brace-initialize a std::array you’re doing aggregate initialization of a class with one member that is itself an aggregate. Normally that would require two sets of braces:
// Braces for std::array
// │ │
// ▼ ▼
std::array<int, 2> arr = { {1, 2} };
// ▲ ▲
// │ │
// Braces for int[2] member
C++ has brace-elision rules to make this situation nicer though. You can omit the inner set of braces, and the initializers will "pass through" to underlying member objects that are themselves aggregates. In this case that’s the std::array‘s int[2] member.
This is the root of your issue. When you initialize an array of My_Struct using braces, the compiler thinks you intended the inner set of braces to initialize the std::array‘s My_Struct[] member rather than the My_Struct within. Since there are two initializers when it was expecting only one it throws an error:
// Initializer for std::array
// │ │
// ▼ ▼
std::array<My_Struct, 2> my_array = { {1, 2}, {2, 3} };
// ▲ ▲ ▲ ▲
// │ │ │ │
// Initializer for unnamed My_Struct[2] │ │
// What is this initializer for? I only have one member to initialize
To avoid this situation you just need to add another set of braces:
// ┌ Initializer for std::array ┐
// │ │
// ▼ ▼
std::array<My_Struct, 2> my_array = { { {1, 2}, {2, 3} } };
// ▲ ▲ ▲ ▲ ▲ ▲
// | │ │ │ │ │
// | |Initializers│ │
// | For array elements │
// | |
// Initializer for unnamed My_Struct[2]