std::vector::resize调用已删除拷贝构造函数问题咨询及求解
哈哈,这个坑我当初踩过!让我一步步给你捋清楚~
问题1:为何已有默认构造的情况下,std::vector::resize仍尝试调用已删除的拷贝构造函数?
你可能忽略了std::vector::resize的一个关键场景:当新的大小超过当前vector的容量(capacity)时,vector需要重新分配内存,这时候要把原有的元素转移到新的内存空间里。
你给Mesh类加了移动赋值运算符,根据C规则,这会隐式删除拷贝构造和拷贝赋值,但更关键的是:如果你只声明了移动赋值,编译器不会自动生成默认的移动构造函数(C标准规定,只有当类没有用户声明的任何拷贝/移动构造、赋值运算符或析构函数时,才会自动生成移动构造)。
这就导致了:当vector需要扩容转移元素时,因为没有移动构造函数可用,编译器会退而求其次尝试调用拷贝构造——但你的拷贝构造已经被删除了,所以编译报错。哪怕Mesh有默认构造,扩容时的元素转移步骤才是触发报错的原因。
问题2:是否因未满足std::vector::resize的某项要求导致失败?
完全是!std::vector::resize的要求不止是value_type可默认构造,它的行为分两种情况:
- 如果新大小
n <= 当前容量:只需要元素是DefaultInsertable(也就是有默认构造),这时候不会触发拷贝/移动; - 如果新大小
n > 当前容量:需要先扩容,然后把原元素转移到新内存,再构造新元素。这时候要求元素是MoveInsertable(支持移动构造)或者CopyInsertable(支持拷贝构造)——你的Mesh类两者都不满足,所以直接失败。
问题3:如何在保留移动赋值运算符的前提下解决该问题?
有三个靠谱的方案,你可以根据需求选:
方案1:显式默认移动构造函数
既然你要保留移动赋值,不如顺便给Mesh加上默认的移动构造,让编译器帮你生成符合要求的移动语义:
class Mesh { public: Mesh() = default; // 你的默认构造 Mesh(Mesh&&) = default; // 显式要求编译器生成默认移动构造 Mesh& operator=(Mesh&&) = default; // 保留你的移动赋值 // 其他成员代码... };
这样vector扩容时会用移动构造转移元素,完全避开拷贝构造,完美解决问题。
方案2:提前预留足够容量
如果你确定要resize到10个元素,可以先调用reserve提前分配足够的内存,这样resize时就不需要扩容,也就不会触发元素转移:
Model myModel; myModel.meshes.reserve(10); // 提前把容量拉到10 myModel.meshes.resize(10); // 现在只需要默认构造新元素,不会碰拷贝/移动构造
这个方案适合你明确知道最终大小的场景,不需要修改Mesh类的定义。
方案3:恢复拷贝构造(如果允许的话)
如果Mesh类本身是可以被拷贝的,你可以显式默认拷贝构造和拷贝赋值,这样既保留移动赋值,又恢复拷贝能力:
class Mesh { public: Mesh() = default; Mesh(const Mesh&) = default; // 恢复拷贝构造 Mesh& operator=(const Mesh&) = default; // 恢复拷贝赋值 Mesh& operator=(Mesh&&) = default; // 保留移动赋值 // 其他成员代码... };
这个方案适合你需要同时支持拷贝和移动语义的场景。
内容的提问来源于stack exchange,提问作者Hakim

