应改用 std::move_only_function,因其仅要求 moveconstructible,不强制拷贝;而 std::function 要求 copyconstructible,导致捕获 unique_ptr 等 move-only 类型的 lambda 编译失败。

遇到 error: use of deleted function 'X::X(const X&)' 就该换 std::move_only_function ——这不是你 lambda 写错了,是 std::function 在构造时就拒绝了 move-only 类型。
为什么 std::function 编译失败而 std::move_only_function 可以
std::function 强制要求被包装对象满足 CopyConstructible;捕获 std::unique_ptr、std::mutex 或自定义 delete 拷贝构造函数的类的 lambda,会自动禁用拷贝构造,编译器直接报错。而 std::move_only_function 只要求 MoveConstructible,绕过拷贝检查,且底层不预留虚函数表项、不维护引用计数字段,属于零开销类型擦除。
- 典型错误不是语法问题,而是语义冲突:lambda 本就不该被拷贝,
std::function却非要它可拷贝 - 即使 lambda 声明为
const,只要内部执行ptr.reset()或std::move(ptr),它仍是 move-only,只能进std::move_only_function - 捕获引用如
[&x]虽然自身可拷贝,但 move 后原闭包失效,x成为悬垂引用,运行时崩溃风险极高
声明和初始化必须手写完整调用签名
std::move_only_function 不提供 make_move_only_function 辅助函数,也不支持 auto 推导;模板参数必须包含返回类型、参数列表、以及全部调用限定符(noexcept、const、&、&&),否则 operator() 行为不匹配,可能引发异常传播失控或调用失败。
- 正确:
std::move_only_function<int const std::string noexcept></int>、std::move_only_function<void const></void> - 错误:
std::move_only_function<void></void>(缺括号与参数列表) - 错误:
std::move_only_function(空模板参数,编译失败) - 错误:
auto f = std::move_only_function{}(模板参数无法推导) - 漏掉
const可能在 const 成员函数中调用失败;漏掉noexcept可能导致本应抑制的异常意外抛出
所有所有权转移操作都必须显式 std::move
std::move_only_function 设计为 “move-first”,编译器绝不隐式转换右值,也没有运行时兜底机制。任何涉及所有权移交的操作(存入 std::vector、传参、赋值)若遗漏 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或逐个std::move成员,否则默认生成的会尝试拷贝 - 从变量构造时也必须显式 move:
std::move_only_function<void> f = std::move(lambda);</void>,直接写f = lambda;是非法拷贝
空值检查和调用后状态管理最容易被忽略
std::move_only_function 支持空初始化(std::move_only_function<void> f;</void>),但调用前必须用 if (f) 判空;且一旦 move 出去(如 std::move(f)),原对象进入有效但未指定状态,再次调用 f() 是未定义行为。
- 它不提供
target()或target_type(),因为 move-only 类型擦除后无法安全还原原始类型,这部分能力被主动舍弃 - 别把它塞进需要
CopyConstructible的老接口(如某些第三方回调注册函数),编译不过就是不过 - 空值检查不是可选项——它不像
std::function那样有较宽松的默认行为,判空缺失直接导致崩溃
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











