std::move_only_function仅接受可移动不可拷贝的闭包,如捕获std::unique_ptr或std::mutex的lambda;普通可拷贝lambda需显式move构造;它避免资源重复释放,适用于独占语义场景。

std::move_only_function 能接住哪些闭包?
它只接受可移动、不可拷贝的闭包,比如捕获了 std::unique_ptr、std::mutex 或带删除拷贝构造函数的自定义类型。普通 lambda(没捕获或只捕获值)默认可拷贝,反而不能直接塞进去——得显式禁用拷贝,或者靠 move 包一层。
- 能接:
[ptr = std::make_unique<int>(42)]() mutable { return *ptr; }</int>(含std::unique_ptr,天然不可拷贝) - 不能直接接:
[x = 42]() { return x; }(可拷贝 lambda),必须写成std::move_only_function<int>{[x = 42]() mutable { return x; }}</int>或先 move 构造 - 注意:捕获引用(如
[&x])本身不阻止拷贝,但若引用的对象生命周期短,move 后原闭包失效,运行时崩
为什么不能用 std::function?
std::function 要求可拷贝,而很多现代回调场景依赖独占语义:比如一个任务持有网络 socket、临时 buffer 或异步状态机,复制一份毫无意义,还可能引发资源争用或 double-close。
- 典型报错:
error: use of deleted function 'MyHandler::MyHandler(const MyHandler&)'—— 当你试图把不可拷贝 handler 塞进std::function -
std::move_only_function底层不存拷贝逻辑,存储成本略低(少一个虚拷贝函数指针),但调用开销和std::function相同 - 兼容性:C++23 才正式加入标准;GCC 13+ / Clang 16+ 支持,MSVC 19.35+;旧编译器需用
std::function+ 手动 move 管理或第三方替代(如folly::Function)
怎么安全地转移闭包所有权?
别想着“传引用”或“共享”,std::move_only_function 的设计哲学就是“移交即放弃”。一旦 move 出去,原变量进入有效但未指定状态,再次调用会触发未定义行为。
- 正确姿势:
auto task = std::move_only_function<void>{[handle = std::move(socket)]() mutable { handle.send("done"); }};</void> - 错误姿势:
task(); task();—— 第二次调用未定义;std::move(task)()也不行,move 后不能再调用 - 如果需要多次执行,闭包内部自己管理状态(如用
mutable+ 成员变量),而不是靠外部重复 move - 传递给异步 API 时,确保接收方明确承担所有权(例如:参数是
std::move_only_function<void> &&</void>)
和 std::packaged_task 混用要注意什么?
两者都处理可调用对象,但语义不同:std::packaged_task 是“带 promise 的可调用体”,调用一次就消费掉;std::move_only_function 是纯调用包装,可多次调用(只要闭包本身支持)。
立即学习“C++免费学习笔记(深入)”;
- 误用示例:
std::packaged_task<void()> t{[]{}}; std::move_only_function<void()> f{std::move(t)}; // 编译失败:packaged_task 不可移动到 move_only_function?等等——其实可以,但 t 已被 move,不能再调用 - 关键区别:
std::packaged_task移动后失效(调用 operator() 报std::future_error),而std::move_only_function移动后原变量失效,但目标仍可调用 - 推荐组合:用
std::move_only_function封装业务逻辑,再用std::packaged_task包一层做 future 绑定,避免混淆所有权边界
std::move_only_function 却忘了闭包内部没做状态保护。










