为何嵌套初始化二维std::array报错?与std::vector差异解析
这个问题的核心在于std::array和std::vector的初始化机制天差地别,再加上MSVC2017对C++标准的严格执行(或者说当时的实现限制),才出现了你看到的差异。咱们一步步拆解:
1. std::array是聚合类型,初始化规则更严格
std::array本质上是C风格数组的包装器,属于C++里的聚合类型。它的底层结构大概长这样(简化版):
template<class T, size_t N> struct array { T _Elems[N]; // 内部是一个固定大小的C数组 // 其他成员函数... };
按照C++11的标准,聚合类型的初始化需要严格匹配它的结构:
- 最外层的
{}用来初始化整个std::array对象 - 中间的
{}用来初始化内部的C数组_Elems - 最内层的
{}用来初始化每个std::array<int,2>元素
所以你原本写的嵌套初始化少了一层括号,正确的写法应该是:
std::array<std::array<int,2>,2> x = {{{0,1},{2,3}}}; // 三层大括号,MSVC2017会认可
MSVC2017严格遵循了早期的标准规则,认为{{0,1},{2,3}}里的两个初始化器是给外层std::array的“成员”用的,但外层std::array的真正成员是内部的C数组,所以编译器判定初始化器数量过多。
而你用{0,1,2,3}能成功,是因为C++允许聚合初始化的括号省略——编译器会自动把平铺的元素对应到内部嵌套数组的每个位置,不需要显式写全所有括号。
2. std::vector不是聚合类型,靠列表构造函数工作
std::vector和std::array完全不同,它不是聚合类型,而是提供了专门的列表构造函数(接受std::initializer_list参数)。当你写:
std::vector<std::vector<int>> y = {{0,1},{2,3}};
编译器会把外层的{}解析为std::initializer_list<std::vector<int>>,里面的每个{0,1}和{2,3}都会单独调用std::vector<int>的列表构造函数,生成对应的子vector。整个过程是通过构造函数调用完成的,和聚合初始化的规则无关,所以自然不会报错。
补充:新编译器/新标准已经放宽了限制
如果你换成MSVC2019及以后的版本,或者符合C++14+标准的编译器,写{{0,1},{2,3}}来初始化二维std::array是可以的——因为后来的标准放宽了聚合初始化的括号省略规则,允许省略中间的那层括号,让写法更直观。但MSVC2017比较老,还没支持这个特性。
内容的提问来源于stack exchange,提问作者PeteUK

