C风格数组成员初始化编译报错,为何与std::array表现不同?
C风格数组与std::array初始化行为差异问题
问题场景
初始化C风格数组成员时遇到编译错误,但将成员改为std::array即可正常编译,代码示例如下:
struct A { A(int aa) : a(aa) {} A(const A &a) = delete; A &operator=(const A &a) = delete; private: int a; std::vector<int> v; }; struct B1 { B1() : a1{{{1}, {2}}} {} // 编译通过(GCC 9.2) private: std::array<A, 2> a1; }; struct B2 { B2() : a2{{1}, {2}} // 编译错误:使用已删除的函数'A::A(const A&)'(GCC 9.2) {} private: A a2[2]; };
疑问与额外观察
- 为何C风格数组和
std::array的编译器行为存在差异?原本以为二者功能基本一致(已知std::array更偏向聚合类型,而非vector那样的纯容器,可能理解有误)。 - 额外观察:
- 若移除A中的
std::vector<int>成员,编译器允许初始化C风格数组; - GCC 9.5无此问题,这是否属于GCC Bug?
- 若移除A中的
确认结论
经确认这是GCC的Bug,详情为:无拷贝构造函数时,数组的大括号初始化有时会失败(该Bug在GCC 4.9中被报告,于GCC 9.4中修复)。
问题解答
核心原因:GCC旧版本的编译实现Bug
在GCC 9.4之前的版本中,对C风格数组的聚合初始化存在实现缺陷:当数组元素类包含**非平凡成员(比如std::vector)**且禁用了拷贝构造函数时,编译器错误地尝试通过拷贝构造来完成数组元素的初始化,而非直接调用元素的构造函数。
而std::array作为标准库定义的聚合类型,其初始化逻辑在GCC中的实现更为规范,能够正确识别并直接调用元素的构造函数完成初始化,因此不会触发拷贝构造函数的错误。
当移除A中的std::vector成员后,类A变为平凡类型,GCC旧版本的数组初始化逻辑可以正常处理,因此编译通过。GCC 9.4及后续版本修复了这个C风格数组初始化的Bug,所以GCC 9.5中不再出现该问题。
内容的提问来源于stack exchange,提问作者avikpram
相关产品推荐
相关产品推荐

