C++14中禁用拷贝构造函数后对象数组初始化编译报错求助
为什么删除拷贝构造函数后,数组列表初始化会触发编译错误?
我来帮你拆解这个问题——你遇到的报错,根源是C++14及更早版本中,数组列表初始化的底层逻辑会调用拷贝构造函数,而你已经把这个函数显式删除了,所以编译器直接报错。
先看你的代码片段:
class A { public: A(int i) :m_n{i} { std::cout << "A(int)\n"; } A(const A&) = delete; // 禁用拷贝构造 private: int m_n; }; int main() { A aa[] {1, 2, 3}; // 这里触发错误 return 0; }
为什么会报错?
在C++14标准下,当你用{1,2,3}初始化数组A aa[]时,编译器的处理步骤是:
- 先通过
A(1)、A(2)、A(3)创建三个临时的A对象; - 接着尝试把这些临时对象拷贝构造到数组的每个元素对应的内存位置中。
但你已经用= delete禁用了拷贝构造函数,这一步自然就不被允许,于是抛出了use of deleted function 'A::A(const A&)'的错误。
怎么解决?
这里有几个可行的方案,你可以根据自己的编译环境选择:
1. 升级到C++17或更高版本
C17对初始化规则做了优化,引入了直接初始化的逻辑:编译器会直接在数组元素的内存位置上调用A(int)构造函数,不再需要创建临时对象然后拷贝。只要把编译标准切换到C17及以上,你的代码就能正常编译运行,会输出三次A(int)。
2. 在C++14中手动构造数组元素
如果必须留在C++14环境,可以用**定位new(placement new)**来手动在数组内存中构造对象,完全绕过拷贝:
#include <iostream> #include <memory> class A { public: A(int i) :m_n{i} { std::cout << "A(int)\n"; } A(const A&) = delete; ~A() = default; // 确保析构函数可访问,方便后续手动销毁 private: int m_n; }; int main() { // 分配足够的内存,alignas确保内存对齐符合A的要求 alignas(A) char buffer[3 * sizeof(A)]; // 把内存地址转换成A*类型 A* aa = reinterpret_cast<A*>(buffer); // 逐个在对应位置构造A对象 new (&aa[0]) A(1); new (&aa[1]) A(2); new (&aa[2]) A(3); // 记得手动调用析构函数清理对象 for (int i = 0; i < 3; ++i) { aa[i].~A(); } return 0; }
3. 改用std::vector配合emplace_back
另一种C++14友好的方式是使用std::vector,通过emplace_back直接在容器内部构造对象,避免拷贝操作:
#include <iostream> #include <vector> class A { public: A(int i) :m_n{i} { std::cout << "A(int)\n"; } A(const A&) = delete; A(A&&) = default; // 可选:默认移动构造,方便vector扩容(如果需要的话) private: int m_n; }; int main() { std::vector<A> vec; vec.reserve(3); // 预分配3个元素的内存,避免后续扩容 vec.emplace_back(1); // 直接在vector内存中构造A(1) vec.emplace_back(2); vec.emplace_back(3); return 0; }
如果已经用reserve预分配了足够内存,甚至可以不用声明移动构造函数——因为不会触发扩容后的元素移动。
总结
这个问题本质是C标准迭代带来的初始化规则差异:C17之前的数组列表初始化依赖拷贝构造,而C++17及以后则支持直接初始化。根据你的编译环境选对应的方案就行。
内容的提问来源于stack exchange,提问作者Julian Zhang
相关产品推荐
相关产品推荐

