直接传 std::unique_ptr 会编译失败,因为其拷贝构造函数被删除,只支持移动语义;需用 std::move 显式转移所有权,否则触发“use of deleted function”错误。

为什么直接传 std::unique_ptr 会编译失败
因为 std::unique_ptr 禁止拷贝,只允许移动。如果你写 void func(std::unique_ptr 并调用 func(ptr)(其中 ptr 是一个已存在的 std::unique_ptr),编译器会报错:use of deleted function ‘std::unique_ptr<_tp _dp>::unique_ptr(const std::unique_ptr<_tp _dp>&)’。这不是语法错误,是设计使然——unique_ptr 的所有权必须明确、唯一。
三种传参方式对应不同语义,别混用
选哪种取决于你是否想在函数内接管资源、是否需要修改原指针、以及调用方后续是否还要用它:
-
按值传递
std::unique_ptr:函数获得所有权,调用方的原ptr必须显式用std::move(ptr)转移,之后ptr变为空(nullptr)。适用于“移交控制权”的场景。 -
按引用传递
const std::unique_ptr:函数只读访问所指对象,不改变所有权,也不影响原& ptr。适合只观察内容、不做释放或重置的操作。 -
按引用传递
std::unique_ptr(非 const):函数可修改& ptr本身(比如reset()、release()、重新赋值),调用方的变量仍有效,但内容可能被改。慎用,容易引发意外释放。
std::move 不是强制转移,只是类型转换
std::move 本身不移动任何内存,只是把左值转成右值引用,让编译器允许调用移动构造函数或移动赋值运算符。常见误用:
- 对同一个
ptr多次调用std::move:第二次之后ptr已为空,再 move 没意义,也不会报错,但逻辑可能崩。 - 传参时漏写
std::move却期望所有权转移:编译直接失败,不是静默忽略。 - 在函数内部对参数
std::unique_ptr再次std::move给其他变量后,自己就失效了——比如auto p2 = std::move(p);后,p不能再解引用或调用get()。
性能与兼容性:移动开销几乎为零,但接口语义必须清晰
std::unique_ptr 移动只是复制原始指针和删除器(若为函数对象且不可复制,则需满足可移动),没有堆分配/释放,所以性能无负担。真正的问题在接口设计:
- 如果函数声明为接收
std::unique_ptr,调用方必须意识到这是“交出所有权”,不能假设调用后还能用原指针。 - 跨模块或 API 边界时,避免返回
std::unique_ptr给裸指针持有者;同样,不要让 C 风格回调持有std::unique_ptr生命周期——它无法参与 RAII。 - 和
std::shared_ptr混用时注意:不能用std::shared_ptr构造函数直接接std::unique_ptr的裸指针(会重复释放),应使用std::shared_ptr完成安全移交。(std::move(u))
最常被忽略的一点:移动之后的状态检查。哪怕只调用一次 std::move,也建议在关键路径上加 assert(!ptr) 或 if (!ptr) throw ...,尤其在调试阶段——空指针解引用不会总立刻 crash,但后果难追溯。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











