std::move_only_function是唯一合法零开销方案,因它仅要求MoveConstructible;std::function强制CopyConstructible导致捕获unique_ptr等non-copyable类型的lambda编译失败,错误为“use of deleted function”。

直接用 std::move_only_function 存捕获 std::unique_ptr、std::mutex 或自定义 non-copyable 类型的 lambda,是唯一合法且零开销的方案;试图塞进 std::function 会立刻编译失败,不是写法问题,是语义冲突。
为什么 std::function 编译失败而 std::move_only_function 可以
错误信息通常是 error: use of deleted function 'X::X(const X&)'——这不是你的 lambda 写错了,是 std::function 强制要求目标类型满足 CopyConstructible,而捕获 std::unique_ptr 的 lambda 自动删除了拷贝构造函数。
std::move_only_function 只要求 MoveConstructible,绕过该约束。它不预留虚函数表项、不引入引用计数、不支持拷贝或 const 调用,也没有 target() 方法——这些不是缺陷,是为 move-only 语义主动舍弃的设计取舍。
- 哪怕 lambda 声明为
const,只要内部调用std::move(ptr)或ptr.reset(),它就自动变成 move-only,只能进std::move_only_function - 捕获
[&x]的 lambda 本身可能仍可拷贝,但std::move后原闭包失效,x成为悬垂引用,运行时崩溃风险极高,不属于安全路径 -
std::function无法从非空实例安全std::move出来(部分实现允许空实例 move,但不可靠);std::move_only_function显式设计为 move-first
std::move_only_function 初始化必须显式写出完整签名
std::move_only_function 不提供 make_move_only_function 辅助函数,也不支持 auto 推导;模板参数必须手写完整调用签名,包括返回类型、参数列表、noexcept、const、&、&& 等所有调用限定符。
- 正确:
std::move_only_function、std::move_only_function - 错误:
std::move_only_function(缺少括号与参数列表) - 错误:
auto f = std::move_only_function(模板参数无法推导){[](){}}; - 若被封装函数带
noexcept,而模板参数未标注,调用时可能意外抛出异常,破坏异常安全契约
所有权转移必须显式调用 std::move
所有涉及“拥有权变更”的操作都强制显式 std::move;编译器不会隐式转换右值,也不会延迟至运行时报错——漏写即编译失败,无警告、无兜底。
- 初始化:
std::move_only_function,不能写f = std::move(lambda); f = lambda; - 存入容器:
std::vector<:move_only_function>> tasks;→tasks.push_back(std::move(task)); - 函数参数建议用右值引用:
void enqueue(std::move_only_function,避免入口处尝试拷贝&&task) - 类成员含
std::move_only_function时,移动赋值操作符需显式定义为= default或逐个std::move成员
调用前必须判空,且 move 后原对象失效
std::move_only_function 支持空初始化(std::move_only_function),但调用前必须用 if (f) 检查是否为空,否则触发未定义行为。
更关键的是:一旦对某个 std::move_only_function 实例调用 std::move,它就进入有效但空的状态(类似 std::unique_ptr move 后为 nullptr),再次调用 operator() 会崩溃。这个状态变化无声无息,容易被忽略。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











