问题
I wonder, why declaration of std_arr in the following code generates an error, while c_arr compiles well:
struct S { int a, b; };
S c_arr[] = {{1, 2}, {3, 4}}; // OK
std::array<S, 2> std_arr = {{1, 2}, {3, 4}}; // Error: too many initializers
Both std::array and S are aggregates. From aggregate initialization on cppreference.com:
If the initializer clause is a nested braced-init-list (which is not an expression and has no type), the corresponding class member is itself an aggregate: aggregate initialization is recursive.
Why this initialization of std::array does not compile?
回答1:
The braces in aggregate initialisation are largely optional, so you can write:
S c_arr[] = {1, 2, 3, 4}; // OK
std::array<S, 2> std_arr = {1, 2, 3, 4}; // OK
If you do add braces, though, then the braces are taken to apply to the next sub-object. Unfortunately, when you start nesting, this leads to silly code being valid, and sensible code like yours being invalid.
std::array<S, 2> std_arr = {{1, 2, 3, 4}}; // OK
std::array<S, 2> std_arr = {1, 2, {3, 4}}; // OK
std::array<S, 2> std_arr = {1, {2}, {3, 4}}; // OK
These are all okay. {1, 2, 3, 4} is a valid initialiser for the S[2] member of std_arr. {2} is okay because it is an attempt to initialise an int, and {2} is a valid initialiser for that. {3, 4} is taken as an initialiser for S, and it's also valid for that.
std::array<S, 2> std_arr = {{1, 2}, {3, 4}}; // error
This is not okay because {1, 2} is taken as a valid initialiser for the S[2] member. The remaining int sub-objects are initialised to zero.
You then have {3, 4}, but there are no more members to initialise.
As pointed out in the comments,
std::array<S, 2> std_arr = {{{1, 2}, {3, 4}}};
also works. The nested {{1, 2}, {3, 4}} is an initialiser for the S[2] member. The {1, 2} is an initialiser for the first S element. The {3, 4} is an initialiser for the second S element.
I'm assuming here that std::array<S, 2> contains an array member of type S[2], which it does on current implementations, and which I believe is likely to become guaranteed, but which has been covered on SO before and is not currently guaranteed.
回答2:
Since the question is tagged C++14, I'll be quoting N4140. In [array] it says that std::array is an aggregate:
2 An
arrayis an aggregate (8.5.1) that can be initialized with the syntaxarray a = { initializer-list };where initializer-list is a comma-separated list of up to
Nelements whose types are convertible toT.
In general, it's agreed that you need an extra pair of outer braces to initialize the underlying aggregate, which looks something like T elems[N]. In paragraph 3, it's explained that this is for exposition purposes and not actually part of the interface. In practice, however, libstdc++ and Clang implement it like that:
template<typename _Tp, std::size_t _Nm>
struct __array_traits
{
typedef _Tp _Type[_Nm];
static constexpr _Tp&
_S_ref(const _Type& __t, std::size_t __n) noexcept
{ return const_cast<_Tp&>(__t[__n]); }
};
template<typename _Tp, std::size_t _Nm>
struct array
{
/* Snip */
// Support for zero-sized arrays mandatory.
typedef _GLIBCXX_STD_C::__array_traits<_Tp, _Nm> _AT_Type;
typename _AT_Type::_Type _M_elems;
Clang:
template <class _Tp, size_t _Size>
struct _LIBCPP_TYPE_VIS_ONLY array
{
/* Snip */
value_type __elems_[_Size > 0 ? _Size : 1];
There are changes between C++11 and C++14 regarding aggregate initialization, however none that would make:
std::array<S, 2> std_arr = {{1, 2}, {3, 4}};
not ill-formed.
来源:https://stackoverflow.com/questions/28541488/nested-aggregate-initialization-of-stdarray