In the following code I'm trying to initialize arrays (raw C-classic and std::array
) minimizing the usage of the element type, i.e. S
:
#include <array>
struct S { unsigned u; int i; };
auto std_array = std::array<S, 3>{
S{0U, 0},
S{1U, 1},
S{2U, 2}
};
S raw_array[] = {
{0U, 0},
{1U, 1},
{2U, 2}
};
/* // compile error: excess elements in struct initializer
std::array<S,3> std_array_no_type = {
{0U, 0},
{1U, 1},
{2U, 2}
};
*/
std::array<S,3> std_array_one_type_only = {
S{0U, 0},
{1U, 1},
{2U, 2}
};
int main() {}
Using the raw_array
I can specify S
only once. But trying the same with std::array
doesn't work (see the commented std_array_no_type
). I have to specify S
type for each or (this is also an interesting part of the question) only for the first element in the initializer list (see std_array_one_type_only
).
So, is there a way to define an initialized std::array
object using the type S
only once? If no, according to which clause of the standard? And why single explicit type S
allows std_array_one_type_only
to be compiled?
About the 1st question,
So, is there a way to define an initialized
std::array
object using the typeS
only once?
You can add another pair of {}
, because std::array
contains an underlying array in fact.
std::array<S,3> std_array_one_type_only = {{{0U, 0}, {1U, 1}, {2U, 2}}};
// ^ ^ for std::array
// ^ ^ for underlying array
// ^ ^ for the 1st element of underlying array
// ^ ^ for the 2nd element of underlying array
// ^ ^ for the 3rd element of underlying array
About the 2nd question,
And why single explicit type
S
allowsstd_array_one_type_only
to be compiled?
In aggregate initialization, the braces for nested initializer lists may be omitted,
If the aggregate initialization uses
copy- (until C++14)
list-initialization syntax (T a = {args..}
or T a {args..} (since C++14)
), the braces around the nested initializer lists may be elided (omitted), in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, and the subsequent initializer clauses are used to initialize the following members of the object.
Given
std::array<S,3> std_array_no_type = {{0U, 0}, {1U, 1}, {2U, 2}};
// ^ ^ for std::array
// ^ ^ for underlying array
The 1st {0U, 0}
would be tried to be used to initialize the whole underlying array, then leads to the error like excess elements in initializer because std::array
doesn't contain any further subobjects.
Given
std::array<S,3> std_array_no_type = {S{0U, 0}, {1U, 1}, {2U, 2}};
// ^ ^ for std::array
// ^ ^ for the 1st element of underlying array
// ^ ^ for the 2nd element of underlying array
// ^ ^ for the 3rd element of underlying array
S{0U, 0}
can't be used to initialize the underlying array (which doesn't satisfy the style of aggregate initialization), then it'll be used to initialize the 1st element of the underlying array, i.e. the above braces-omitting rule gets applied, then the following {1U, 1}
and {2U, 2}
are used to initialize the following members of the underlying array.