std::array 的嵌套聚合初始化
Posted
技术标签:
【中文标题】std::array 的嵌套聚合初始化【英文标题】:Nested aggregate initialization of std::array [duplicate] 【发布时间】: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::array
和 S
都是聚合。来自aggregate initialization on cppreference.com:
如果初始化子句是一个嵌套的braced-init-list(它不是一个表达式并且没有类型),那么对应的类成员是 本身就是一个聚合:聚合初始化是递归的。
为什么std::array
的这个初始化不能编译?
【问题讨论】:
它应该是std::array<S, 2> 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<S, 2> std_arr 1, 2, 3, 4;
没有=
,因此无论如何它与这个特定问题无关。 :)
【参考方案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_arr
的 S[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<S, 2>
包含S[2]
类型的数组成员,它在当前实现中执行,并且我相信它可能会得到保证,但之前已经在 SO并且目前无法保证。
【讨论】:
我现在看到了。合乎逻辑,但非常不明显。谢谢。 如果你想省略b
的初始化器;例如std::array<S, 2> 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;
格式不正确。
【讨论】:
以上是关于std::array 的嵌套聚合初始化的主要内容,如果未能解决你的问题,请参考以下文章
在 g++ 上进行聚合初始化的 std::array 会生成大量代码
为啥 std::array<std::pair<int,int>, 3> 不能使用嵌套初始化列表初始化,但 std::vector<std::pair<int,in