9

我想知道,为什么std_arr在下面的代码中声明会产生错误,而c_arr编译得很好:

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

两者std::array都是S聚合。从cppreference.com 上的聚合初始化

如果初始化子句是嵌套的花括号初始化列表(它不是表达式并且没有类型),则相应的类成员本身就是一个聚合:聚合初始化是递归的。

为什么这个初始化std::array不编译?

4

2 回答 2

8

聚合初始化中的大括号在很大程度上是可选的,因此您可以编写:

S c_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
std::array<S, 2> std_arr = {1, 2, {3, 4}};  // OK
std::array<S, 2> std_arr = {1, {2}, {3, 4}};  // OK

这些都没问题。{1, 2, 3, 4}是 的S[2]成员的有效初始化程序std_arr{2}没关系,因为它试图初始化一个int,并且{2}是一个有效的初始化器。{3, 4}被视为 的初始化器S,它也适用于此。

std::array<S, 2> std_arr = {{1, 2}, {3, 4}};  // error

这是不行的,因为{1, 2}它被视为S[2]成员的有效初始化程序。剩余的int子对象初始化为零。

然后你有{3, 4},但没有更多的成员要初始化。

正如评论中指出的那样,

std::array<S, 2> std_arr = {{{1, 2}, {3, 4}}};

也有效。嵌套{{1, 2}, {3, 4}}S[2]成员的初始化器。这{1, 2}是第一个S元素的初始化器。这{3, 4}是第二个S元素的初始化器。

我在这里假设它std::array<S, 2>包含一个 type 的数组成员S[2],它在当前的实现中执行,并且我相信它可能会得到保证,但是之前已经在 SO 中介绍过,目前还不能保证。

于 2015-02-16T12:46:04.367 回答
4

由于问题标记为 C++14,我将引用 N4140。在 [array] 它说这std::array是一个聚合:

2 Anarray是一个聚合(8.5.1),可以用语法初始化

  数组 a = {初始化列表};

其中initializer-list是以逗号分隔的列表,最多可N包含其类型可转换为的元素T

一般来说,您需要一对额外的大括号来初始化底层聚合,它看起来像T elems[N]. 在第 3 段中,解释说这是为了说明目的,实际上并不是接口的一部分。然而,在实践中,libstdc++ 和 Clang 是这样实现的:

  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;

铛:

template <class _Tp, size_t _Size>
struct _LIBCPP_TYPE_VIS_ONLY array
{
    /* Snip */

    value_type __elems_[_Size > 0 ? _Size : 1];

C++11 和 C++14 之间在聚合初始化方面有一些变化,但没有任何变化:

std::array<S, 2> std_arr = {{1, 2}, {3, 4}};

不病态。

于 2015-02-16T13:08:46.713 回答