std::move_only_function 是 C++23 中唯一能安全、零开销封装 non-copyable 资源的异步任务类型;它仅要求 MoveConstructible,绕过 std::function 的 CopyConstructible 约束,且需显式书写完整签名、mutable lambda、强制 std::move 转移所有权,并判空调用。

直接说结论:std::move_only_function 是 C++23 中唯一能安全、零开销封装捕获 std::unique_ptr、std::mutex 等 non-copyable 资源的异步任务的类型;用 std::function 必编译失败,用 std::shared_ptr<:function></:function> 则引入不必要开销和语义混乱。
为什么 std::function 编译失败而 std::move_only_function 可以
根本原因是约束不同:std::function 要求被包装对象满足 CopyConstructible,而捕获 std::unique_ptr 的 lambda 会自动删除拷贝构造函数——编译器报错不是你写错了,是语义冲突:
error: use of deleted function 'MyLambda::MyLambda(const MyLambda&)'- 哪怕 lambda 声明为
const,只要内部调用了std::move(ptr)(比如在mutablelambda 中),它就只满足MoveConstructible,无法通过std::function构造验证 -
std::move_only_function显式绕过拷贝检查,只接管所有权,不预留虚函数表项、不引入引用计数字段,真正零开销
声明与初始化必须显式写出完整调用签名
std::move_only_function 不提供 make_move_only_function,也不支持 auto 推导;模板参数必须手写完整函数类型,漏掉任一修饰符都会导致 operator() 行为异常或编译失败:
- 正确写法:
std::move_only_function<int const std::string noexcept></int>、std::move_only_function<void const></void>、std::move_only_function<double></double> - 错误写法:
std::move_only_function<int></int>(缺括号与参数)、std::move_only_function<void></void>(不是完整函数类型)、auto f = std::move_only_function<void>{[](){}};</void>(模板参数无法推导) - lambda 必须声明为
mutable才能移动捕获的std::unique_ptr,否则即使签名对了,也可能因无法修改捕获对象而被拒绝
所有所有权转移操作都强制要求 std::move
编译器不会隐式执行移动,也不会延迟报错——任何遗漏都直接触发编译失败,且无警告:
立即学习“C++免费学习笔记(深入)”;
- 存入容器时:
std::vector<:move_only_function>> tasks;</:move_only_function>→ 必须写tasks.push_back(std::move(task));,写tasks.push_back(task);直接报错 - 函数传参时:形参应为右值引用,如
void enqueue(std::move_only_function<void> &&task)</void>;调用必须写enqueue(std::move(task)); - 类成员持有时:默认生成的移动赋值操作符可能尝试拷贝,必须显式定义为
= default或手动m_task = std::move(other.m_task); - 调用前必须判空:
if (f) { f(); },因为std::move_only_function支持空状态,且不提供target()或target_type()还原原始类型
最易被忽略的是签名完整性与 mutable 的配合:缺一个 noexcept、少一个 const、或者忘了 mutable,都可能导致看似合法的 lambda 无法构造成功,且错误信息往往指向“无法匹配构造函数”而非具体缺失项。


















