【问题标题】:Nested aggregate initialization of std::array [duplicate]std::array 的嵌套聚合初始化
【发布时间】:2015-04-16 23:13:17
【问题描述】:

我想知道,为什么在下面的代码中声明 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

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

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

【问题讨论】:

  • 它应该是 std::array&lt;S, 2&gt; std_arr{{ {1, 2}, {3, 4} }}; - 围绕构造函数参数的外部参数,初始化列表的下一对,每个 S 元素的内部对。 C++14 将使它与更少的一组外部{ } 一起工作。 (= 是可选的。)
  • @remyabel 聚合初始化的规则在 C++11 和 C++14、IIRC 之间发生了变化,我不清楚它们在这里不相关。
  • @hvd 不过大括号省略没有变化。
  • 你说:“嘿,我读了标准,它说我应该能够做 X,但是当我做我的编译器时会哭”。您不认为指定编译器和您正在使用的版本可能相关吗?
  • @remyabel 啊,这个几乎重复的答案给出了答案:大括号省略在 C++11 和 C++14 之间没有变化,但在后 C 中确实发生了变化应用于 C++11 的 ++11 个 DR。因此,您在 -std=c++11-std=c++14 的编译器中看不到差异是正确的:即使在 C++11 模式下也应用了 DR 分辨率。除此之外,它涵盖了std::array&lt;S, 2&gt; std_arr {1, 2, 3, 4}; 没有=,因此无论如何它与这个特定问题无关。 :)

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


【解决方案1】:

聚合初始化中的大括号在很大程度上是可选的,所以你可以这样写:

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},但没有更多的成员要初始化。

正如 cmets 中所指出的,

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

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

我在这里假设std::array&lt;S, 2&gt; 包含S[2] 类型的数组成员,它在当前实现中执行,并且我相信它可能会得到保证,但之前已经在 SO并且目前无法保证。

【讨论】:

  • 我现在看到了。合乎逻辑,但非常不明显。谢谢。
  • 如果你想省略 b 的初始化器;例如std::array&lt;S, 2&gt; g = { {1}, {3} }; 希望得到g[1].a == 3 ...然后似乎所有奇怪的建议都无法使用,双支撑是唯一的选择。对于 C++14 来说就这么多了,据说 std::array 不需要双支撑 ...
【解决方案2】:

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

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

 数组 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}};

格式不正确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 2012-02-10
    • 2021-03-19
    • 1970-01-01
    相关资源
    最近更新 更多