为什么C++中std::vector和std::array的initializer_list行为不同?

88

代码:

std::vector<int> x{1,2,3,4};
std::array<int, 4> y{{1,2,3,4}};

为什么我需要在std::array中使用双花括号?


1
你是否真的需要std::array的第二组大括号,还是只是收到了警告?对我来说,std::array<int,4> y{1,2,3,4};是可以工作的。 - bames53
3
GCC 在编译时出现错误。 - Xeo
14
@Xeo:使用警告编译不规范的程序并不是“错误”的。 - Steve Jessop
3
@Steve: 确实如此。我们可以说不便携吗? - Xeo
2
@Xeo:是的,我无论如何都会为我编写的代码使用“-Werror”,所以它不会影响我的可移植性。其他人的情况可能不同,如果他们是轻量级的开发者或需要包含由轻量级开发者编写的头文件 :-) - Steve Jessop
@bames53:是的,对我也有效。 - Destructor
2个回答

75

std::array<T, N>是一个聚合类型: 它没有任何用户声明的构造函数,甚至没有一个使用std::initializer_list的构造函数。使用花括号进行初始化时,采用的是C++中从C继承而来的聚合初始化特性。

"旧风格"的聚合初始化使用等号=

std::array<int, 4> y = { { 1, 2, 3, 4 } };

使用这种旧式的聚合初始化方式,多余的大括号可以省略,因此这等同于:

std::array<int, 4> y = { 1, 2, 3, 4 };

然而,这些额外的大括号只能在声明形式为T x = { a };时省略(C++11 §8.5.1/11),也就是说,当使用旧式的=时。允许省略花括号的规则不适用于直接列表初始化。这里的一个脚注指出:“在列表初始化的其他用法中不能省略花括号。”
关于这个限制有一个缺陷报告:CWG defect #1270。如果采用了建议的解决方案,则允许省略花括号用于其他形式的列表初始化,并且以下内容将是良好的:
std::array<int, 4> y{ 1, 2, 3, 4 };

(感谢Ville Voutilainen发现了这个缺陷报告。)

3
那么,这是因为呈现给“数组”抽象模型的失败吗? - Mark Ransom
1
@Mehrdad:它确实是一致的。不一致的是您正在初始化两种完全不同的类型。 - Nicol Bolas
5
@NicolBolas: 我原本认为“一致性”的意义在于使用相同的初始化语法来处理不同类型。 (是的,我明白发生了什么......我只是想说无论是否解释,对用户而言这都不是“一致的”)。 - user541686
@MarkRansom:这其实是语言的一种怪癖,因为std::array<int> y = {1,2,3,4};虽然会收到Clang的建议使用花括号进行初始化,但仍能正常工作,而不会像“在使用直接列表初始化时省略子对象初始化的花括号”那样产生严格错误。 - Xeo
1
@Xeo:实际上,在聚合初始化中允许使用大括号省略语法,但是(显然)在使用直接列表初始化语法时不允许使用。 - James McNellis
显示剩余2条评论

33
因为 std::vector 提供了一个接受 std::initializer_list<T> 的构造函数,而 std::array 没有构造函数,在初始化列表 {1, 2, 3, 4} 中实际上不被解释为 std::initializer_list,而是用于初始化 std::array 内部 C 风格数组的聚合初始化(这就是第二组大括号的含义:一组用于 std::array,一组用于内部 C 风格成员数组)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接