c++oding="utf-8" ?>
std::move_only_function是c++23专为不可拷贝可调用对象设计的仅可移动包装器,要求moveconstructible而非copyconstructible,需显式声明完整调用签名,所有转移必须手动std::move,不支持target()和栈上存储保证。

编译失败时看到 error: use of deleted function 'X::X(const X&)',基本就是该换 std::move_only_function 了——它不是“std::function 的轻量版”,而是唯一能接住不可拷贝闭包的容器。
为什么 std::function 编译不过,而 std::move_only_function 可以
因为 std::function 要求被包装类型满足 CopyConstructible;而捕获 std::unique_ptr 或 std::mutex 的 lambda 会自动禁用拷贝构造,std::function 直接拒收。但 std::move_only_function 只要求 MoveConstructible,绕过拷贝检查。
- 典型错误不是你写错了 lambda,而是语义冲突:编译器在构造时就拒绝,不给你运行机会
- 即使 lambda 声明为
const,只要内部std::move了捕获对象(如mutable+std::move(ptr)),它仍是 move-only,只能进std::move_only_function - 它不支持
target()或target_type()——move-only 类型擦除后无法安全还原原始类型,这部分能力被主动舍弃
怎么声明和初始化 std::move_only_function
模板参数必须显式写出完整调用签名,含 noexcept、const、& 等修饰符;没有 make_move_only_function 辅助函数,也不能靠 auto 推导。
- ✅ 正确:
std::move_only_function<int const std::string noexcept></int>f = [ptr = std::make_unique(42)](int x, const std::string& s) mutable noexcept { return x + *ptr + s.size(); }; - ❌ 错误:
auto f = std::move_only_function{}(模板参数无法推导) - ❌ 错误:
std::move_only_function f = lambda(缺少模板参数,编译失败) - 空初始化合法:
std::move_only_function<void> f;</void>,但调用前必须用if (f)检查是否为空
往 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>,避免在入口就尝试拷贝 - 若封装进类成员,移动赋值操作符需显式定义为
= default或逐个std::move成员 - 捕获引用(如
[&x])本身不阻止拷贝,但std::move后原闭包仍持有悬垂引用——运行时崩,不是编译时报错
最常被忽略的是:它不保证栈上存储,小 callable 可能原地构造,但一旦超过小缓冲区容量(libc++ 默认 24 字节,libstdc++ 通常 32 字节),仍会堆分配;且所有调用点都得手动 std::move,漏一处就卡死在编译期。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











