std::move_only_function专为不可拷贝类型设计,仅要求MoveConstructible,不支持拷贝、类型还原及自动推导,使用时须显式声明签名、手动move、避免重复调用。

std::move_only_function 不是用来“替代” std::function 的,而是专为那些根本不能拷贝的任务类型存在的——比如捕获了 std::unique_ptr、std::mutex 或自定义 delete 拷贝构造函数的类的 lambda。你一旦遇到 error: use of deleted function 'X::X(const X&)',基本就是该换它了。
为什么 std::function 编译失败而 std::move_only_function 可以
因为 std::function 要求被包装对象满足 CopyConstructible;而捕获 std::unique_ptr 的 lambda 天然禁用拷贝构造——编译器直接拒绝。但 std::move_only_function 只要求 MoveConstructible,绕过该约束。
- 典型错误不是你写错了,是语义冲突:lambda 本身不可拷贝,
std::function却非要它可拷贝 - 即使 lambda 带
mutable且内部移动了捕获对象,只要它没显式删除拷贝,std::function仍可能接受(但行为危险);而std::move_only_function强制只走移动路径,更安全 - 没有
target()或target_type()—— move-only 类型擦除后无法安全还原原始类型,这部分能力被主动舍弃
声明和初始化必须显式写出完整调用签名
std::move_only_function 没有 make_move_only_function 辅助函数,也不能靠 auto 推导;模板参数必须包含全部修饰符:返回类型、参数列表、noexcept、&、const 等。
- 正确:
std::move_only_function<void noexcept></void>、std::move_only_function<int const></int> - 错误:
std::move_only_function<void></void>(缺少括号和参数)、std::move_only_function<decltype></decltype>(不能推导) - 漏掉
const可能导致在 const 成员函数中调用失败,因为生成的operator()()默认不带const限定
往 vector 或作为函数参数传递时必须显式 std::move
所有涉及所有权转移的地方,都必须手动调用 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>,避免入口就尝试拷贝 - 若封装进类成员,移动赋值操作符需显式定义为
= default或逐个std::move成员,否则默认生成的会尝试拷贝 - 别把它塞进需要
CopyConstructible的老接口(如某些第三方回调注册函数),编译不过就是不过
空对象大小与堆分配行为容易被误判
空 std::move_only_function 通常占 16 字节(两个指针),比 std::function(常为 32 字节)小,但这只是表象——关键在于它省掉了拷贝逻辑所需的虚函数表项、引用计数、SBO 对齐填充和析构守卫。
- 小 callable(如无捕获 lambda)可能原地构造,不触发堆分配;但一旦超过小缓冲区容量(libc++ 默认 24 字节,libstdc++ 通常 32 字节),仍会 fallback 到堆分配
- 不能假设 “move-only 就一定栈上存”:得查
sizeof和你所用 STL 实现的小缓冲定义(如 libc++ 的__small_size) - 调用开销与
std::function相当(一次虚函数调用),但构造/移动更轻量;性能优势主要体现在避免冗余拷贝逻辑,而非调用路径
最常被忽略的一点是:move 后的 std::move_only_function 进入有效但未指定状态,再次调用 operator()() 是未定义行为——它不是“可重用”,而是“移交即放弃”。如果你需要多次执行,闭包内部自己管理状态(比如用 mutable + 成员变量),而不是靠外部反复 move。


















