std :: array的嵌套聚合初始化

时间:2015-02-16 12:29:43

标签: c++ arrays initialization c++14 aggregate-initialization

我想知道,为什么在以下代码中声明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::arrayS都是聚合。来自aggregate initialization on cppreference.com

  

如果initializer子句是嵌套的braced-init-list(不是表达式且没有类型),则相应的类成员是   本身是一个聚合:聚合初始化是递归的。

为什么std::array的初始化不能编译?

2 个答案:

答案 0 :(得分: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>包含S[2]类型的数组成员,它在当前实现中执行,我相信它可能会得到保证,但是已经在SO上涵盖,目前尚未得到保证。

答案 1 :(得分:3)

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

  

2 array是一个可以使用。初始化的聚合(8.5.1)   语法

  array a = { initializer-list };
     

其中 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}};

没有形成错误。