应改用 std::move_only_function:当 lambda 捕获 unique_ptr、mutex 等 non-copyable 类型导致 std::function 构造失败时,必须用显式签名声明的 std::move_only_function,并通过 std::move 插入容器、判空后调用。

当你在 C++23 中编写回调系统,尝试把捕获了 std::unique_ptr、std::mutex 或自定义 non-copyable 类型的 lambda 注册进事件分发器时,编译器突然报错 error: use of deleted function 'X::X(const X&)',这不是语法写错了,而是 std::function 的 CopyConstructible 约束与你的 lambda 语义根本冲突——此时必须切换到仅接受 MoveConstructible 的 std::move_only_function。
识别 std::function 编译失败的根本原因
第一步:确认错误信息是否含 use of deleted function 字样,并检查 lambda 是否捕获了 std::unique_ptr、std::thread、std::mutex 或显式删除了拷贝构造函数的类。
第二步:若 lambda 内部调用 ptr.reset() 或 std::move(ptr),即使声明为 const,其拷贝构造函数也已被隐式删除——这并非可绕过的问题,而是编译期强制拒绝。
第三步:立即停用所有 std::function<...> 声明,因为 【std::function 对此类 lambda 的构造失败不可修复,任何 cast、wrapper 或临时 std::move 都无法改变该约束】。
立即学习“C++免费学习笔记(深入)”;
正确声明并初始化 std::move_only_function
方法一:手写完整调用签名(必须包含返回类型、参数列表、noexcept、const、& 或 &&)
std::move_only_function<int(const std::string&, int) const noexcept> f = [ptr = std::make_unique<int>(42)](const std::string& s, int x) const noexcept { return x + *ptr + s.size(); };
方法二:禁止简写或依赖推导
【std::move_only_function<void> f = lambda; 是非法的——模板参数缺失括号与参数列表,编译器直接拒绝;auto f = std::move_only_function<>{}; 同样非法,模板参数无法推导。
方法三:空初始化合法,但后续必须判空
std::move_only_function<void()> callback; → 此时 callback 为空状态,直接调用会未定义行为,后续使用前必须先 if (callback) callback();。
将 move-only 回调存入容器并安全执行
① 声明容器:使用 std::vector<std::move_only_function<void()>>,它不支持左值插入。
② 插入操作:必须显式移动——tasks.push_back(std::move(callback));。写成 tasks.push_back(callback); 会触发编译错误,因容器元素类型不可拷贝。
③ 执行前检查:遍历容器时,每个元素都需判空,for (auto& cb : tasks) { if (cb) cb(); }。漏掉判空可能导致调用空函数对象,引发未定义行为。
④ 注意:move 后原变量进入有效但未指定状态,【不可再次调用、不可再次 std::move、不可取地址用于调试断言】。
作为函数参数传递 move-only 回调
函数签名应直接接收右值引用:
void register_callback(std::move_only_function<void()>&& cb) { callbacks.push_back(std::move(cb)); }
调用时必须传右值:register_callback(std::move(my_callback));。
若传左值(如 register_callback(my_callback);),编译器会在入口处尝试拷贝,立刻报错。


















