std::move_only_function不能替代std::function,因其仅支持MoveConstructible而禁止拷贝,专用于捕获unique_ptr、mutex等不可拷贝资源的lambda或禁用拷贝的functor;它模板参数需显式包含const/noexcept等修饰符,无make辅助函数,不支持target()反射,空对象通常16字节,调用开销与std::function相当但移动更轻量。

它不能替代 std::function,但当你手上有捕获了 std::unique_ptr、std::mutex 或自定义 delete 拷贝构造函数的 lambda 时,std::move_only_function 是唯一能接住它的标准工具。
为什么 std::function 编译失败,而 std::move_only_function 可以
根本原因是语义约束不同:std::function 要求被包装类型满足 CopyConstructible;而捕获 std::unique_ptr 的 lambda 会自动删除拷贝构造函数,只保留移动能力。编译器报错通常是:
error: use of deleted function 'MyLambda::MyLambda(const MyLambda&)'
这不是你写错了,是类型系统在拒绝不安全操作。此时换用 std::move_only_function 就能过——它只要求 MoveConstructible,绕过了拷贝检查。
- 哪怕 lambda 是
const限定或带noexcept,也必须显式写进模板参数,比如std::move_only_function<int const noexcept></int> -
std::function允许隐式转换(如函数指针转),std::move_only_function不支持,必须显式构造或移动 - 空的
std::move_only_function调用会直接导致未定义行为,不像std::function会抛异常
声明和初始化时最容易漏掉的三件事
它没有 make_move_only_function 辅助函数,也不能靠 auto 推导,所有信息都得手动写全:
立即学习“C++免费学习笔记(深入)”;
- 模板参数必须是完整调用签名:包括返回类型、参数列表、
const、&、&&、noexcept—— 少一个修饰符都可能编译失败 - 初始化必须用右值:不能写
std::move_only_function<void> f = []{};</void>,得写成std::move_only_function<void> f = []{};</void>或更安全地加std::move包一层 - 普通无捕获 lambda 默认可拷贝,反而不能直接赋给
std::move_only_function;必须显式 move 构造,例如:std::move_only_function<void> f{std::move([]{});}</void>
往 vector 里 push 或传参时为什么总报错
因为所有“拥有权转移”场景都强制要求显式 std::move,编译器不会帮你推导左/右值性:
-
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>,避免入口就尝试拷贝 - 类成员含
std::move_only_function时,移动赋值操作符不能省略,必须写成= default或手动移动每个成员 - 别把它塞进老接口(比如某些回调注册函数)——那些地方隐含要求
CopyConstructible,编译不过就是不过
它真的比 std::function 更轻量吗
空对象大小确实小:典型实现中 std::move_only_function 占 16 字节(两个指针),std::function 常为 32 字节。但这只是表象。
关键差异在逻辑层面:它彻底砍掉了虚函数表里的拷贝相关项、引用计数、SBO 对齐填充和析构守卫。小 callable(如无捕获 lambda)可能原地构造,不触发堆分配;但一旦超过小缓冲区容量(libc++ 默认 24 字节),仍会堆分配。
调用开销和 std::function 相同,移动开销接近 memcpy。性能收益主要来自语义精简,而非微观指令数。
最易被忽略的一点:它不提供 target() 或 target_type(),因为 move-only 类型擦除后无法安全还原原始类型——如果你依赖反射做类型判断,这条路走不通。


















