C++17含std::unique_ptr的延迟创建器编译错误原因与解决方法
问题原因分析
你的代码编译失败的核心原因在于std::unique_ptr的特性:作为独占所有权的智能指针,它的拷贝构造和拷贝赋值运算符被C++标准显式删除,目的是防止多个unique_ptr同时管理同一块内存导致的双重释放问题。
当结构体包含std::unique_ptr时,编译器会自动删除该结构体的默认拷贝赋值运算符(因为成员的拷贝操作不可用)。而你的延迟创建器代码中,赋值操作大概率是直接对左值(保存的原始对象)进行赋值,编译器会尝试调用拷贝赋值运算符,但该运算符已被删除,因此触发编译错误。
解决方案
根据你是否需要保留原始对象的可用性,有两种修改方向:
1. 不需要保留原始对象:触发移动赋值
如果原始对象在赋值后不再被使用,直接用std::move()将其转换为右值,这样编译器会优先调用结构体默认生成的移动赋值运算符(包含unique_ptr的结构体默认会生成合法的移动操作,因为unique_ptr支持移动语义)。
错误代码示例
#include <memory> struct Data { std::unique_ptr<int> val; }; class DelayedCreator { private: Data original; public: DelayedCreator(Data d) : original(std::move(d)) {} void assignTo(Data* new_obj) { // 这里尝试调用拷贝赋值,而Data的拷贝赋值已被删除 *new_obj = original; } }; int main() { Data d; d.val = std::make_unique<int>(42); DelayedCreator creator(std::move(d)); Data* pool_obj = new Data(); creator.assignTo(pool_obj); return 0; }
修改后代码
#include <memory> #include <utility> // 引入std::move struct Data { std::unique_ptr<int> val; }; class DelayedCreator { private: Data original; public: DelayedCreator(Data d) : original(std::move(d)) {} void assignTo(Data* new_obj) { // 转换为右值,触发移动赋值 *new_obj = std::move(original); } }; int main() { Data d; d.val = std::make_unique<int>(42); DelayedCreator creator(std::move(d)); Data* pool_obj = new Data(); creator.assignTo(pool_obj); return 0; }
2. 需要保留原始对象:实现克隆逻辑
如果需要复用原始对象,就不能移动它,此时需要为结构体手动实现克隆方法,复制内部的资源状态(比如对unique_ptr指向的内容进行深拷贝)。
修改后代码示例
#include <memory> struct Data { std::unique_ptr<int> val; // 实现克隆方法,深拷贝内部资源 Data clone() const { Data res; if (val) { res.val = std::make_unique<int>(*val); } return res; } }; class DelayedCreator { private: Data original; public: DelayedCreator(Data d) : original(std::move(d)) {} void assignTo(Data* new_obj) { // 克隆原始对象,避免移动或拷贝 *new_obj = original.clone(); } }; int main() { Data d; d.val = std::make_unique<int>(42); DelayedCreator creator(std::move(d)); Data* pool_obj1 = new Data(); creator.assignTo(pool_obj1); // 可以继续复用原始对象创建新实例 Data* pool_obj2 = new Data(); creator.assignTo(pool_obj2); return 0; }
额外注意事项
- 如果你的结构体还有其他自定义成员,确保移动操作或克隆逻辑能正确处理所有成员。
- 内存池分配的对象要确保正确初始化(比如默认构造后再赋值),避免未定义行为。
内容的提问来源于stack exchange,提问作者VisualGMQ
相关产品推荐
相关产品推荐

