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

Nested aggregate initialization of std::array

本文关键字:初始化 array std 嵌套      更新时间:2023-10-16

我想知道,为什么在以下代码中声明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都是聚合。从聚合初始化到 cppreference.com:

如果初始值设定项子句是嵌套的大括号初始化列表(它不是表达式,也没有类型(,则相应的类成员为 本身是一个聚合:聚合初始化是递归的。

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

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

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}std_arrS[2] 成员的有效初始化器。 {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 上涵盖过,目前无法保证。

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

2 array是一个聚合 (8.5.1(,可以使用 语法

数组 a = { 初始值设定项列表 };

其中初始值设定项列表是最多包含 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}};

不是格式错误。

相关文章: