必须显式指定删除器类型作为第二模板参数,否则编译失败;正确写法有两种:一是用decltype或函数指针显式声明类型并传入可调用对象,二是通过类型别名封装简化使用。

unique_ptr 自定义 deleter 的两种写法
必须在模板参数里显式声明删除器类型,否则 unique_ptr 默认用 delete,传入函数对象或 lambda 都会编译失败。
正确写法只有两种:
- 模板参数指定删除器类型,构造时传入可调用对象(函数指针、lambda、functor)
- 用类模板特化或别名封装,避免每次写冗长的模板参数
例如:
auto del = [](int* p) { std::printf("free %p\n", (void*)p); free(p); };<br>std::unique_ptr<int decltype> ptr(static_cast<int>(malloc(sizeof(int))), del);</int></int>
lambda 做 deleter 为什么经常编译不过
因为 lambda 类型是独有、不可名状的,不同 lambda 表达式即使内容相同,类型也不同。直接写 std::unique_ptr<int auto></int> 是非法的——auto 不能用于模板参数推导。
常见错误写法:
auto d = [](...){...};<br>std::unique_ptr<int auto> p(nullptr, d); // ❌ 编译错误</int>
解决办法只有两个:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 用
decltype(d)显式写出类型(最常用) - 把 lambda 转成函数指针(仅限无捕获 lambda,且签名匹配
void(*)(T*))
无捕获 lambda 转函数指针示例:
void my_free(int* p) { free(p); }<br>// 或<br>std::unique_ptr<int void> ptr(nullptr, [](int* p) { free(p); });</int>
自定义 deleter 影响 move 和 reset 的行为
删除器是 unique_ptr 对象的一部分,会被移动(move),也会被拷贝(仅当删除器本身可拷贝)。这意味着:
- move 构造/赋值后,原
unique_ptr的 deleter 也被转移过去,原对象变成空(deleter 不再有效) - 如果 deleter 含状态(比如带捕获的 lambda 或 functor 成员变量),它必须满足 MoveConstructible;否则
reset()或 move 可能编译失败 - 用
std::function包装 deleter?不推荐——它引入堆分配和虚调用开销,且std::function不是 trivially copyable,会影响unique_ptr的某些优化场景
实际项目中怎么封装更安全
手写每次带 decltype 太啰嗦,也容易出错。推荐用类型别名或专用 wrapper:
比如封装 malloc/free 场景:
template<typename t><br>using malloc_unique_ptr = std::unique_ptr<t void>;<br><br>malloc_unique_ptr<int> p(static_cast<int>(malloc(sizeof(int))),<br> [](int* p) { free(p); });</int></int></t></typename>
注意点:
- 别名里用函数指针类型,比 lambda +
decltype更易读、可复用 - 确保 delete 行为与分配方式严格匹配(new ↔ delete,malloc ↔ free,new[] ↔ delete[])
- 如果需要传递额外上下文(如 logging tag),必须用 functor 类或带捕获 lambda +
decltype,此时务必确认该类型支持移动
最常被忽略的是:删除器的 noexcept 属性没对齐。如果自定义 deleter 抛异常,而 unique_ptr 正在栈展开中析构,程序会直接 terminate——所以生产环境的 deleter 应该显式标记 noexcept。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










