std::move_only_function是C++23引入的仅可移动函数包装器,专为捕获unique_ptr等非拷贝类型的lambda设计,不满足CopyConstructible,仅支持MoveConstructible/MoveAssignable,调用语法与std::function一致但不可互赋值。

std::move_only_function 是什么,为什么不能复制
std::move_only_function 是 C++23 引入的类型擦除函数包装器,专为不可复制(但可移动)的可调用对象设计。它和 std::function 的关键区别在于:不满足 CopyConstructible,只满足 MoveConstructible 和 MoveAssignable。这意味着一旦你把一个 lambda 捕获了 std::unique_ptr 或其他非拷贝类型,就只能用 std::move_only_function 来存——std::function 会直接编译失败。
常见错误现象:error: use of deleted function 'std::function<...>::function(const std::function<...>&)'</...></...>,通常出现在试图把带移动语义捕获的 lambda 赋给 std::function 时。
- 它底层不依赖
std::any或虚函数表的拷贝逻辑,而是用 move-only 存储策略(如小缓冲 + 堆分配 + 移动构造) - 不支持
target()、target_type()等反射接口,因为这些在 move-only 场景下难以安全实现 - 调用语法完全一致:
f(1, "hello"),和std::function无差异
怎么声明和初始化 std::move_only_function
声明方式和 std::function 类似,但必须用移动语义初始化。不能用拷贝构造,也不能隐式转换(除非源本身是右值)。
auto lambda = [ptr = std::make_unique<int>(42)]() mutable {
return *ptr + 1;
};
std::move_only_function<int()> f = std::move(lambda); // ✅ 正确
// std::move_only_function<int()> g = lambda; // ❌ 编译失败:试图拷贝 lambda
- 模板参数必须显式写出调用签名,例如
std::move_only_function<void double></void>,不支持自动推导(没有make_move_only_function辅助函数) - 可以接收任何可移动的可调用物:带移动捕获的 lambda、
std::unique_ptr包裹的函数对象、自定义 move-only functor - 从
std::function转换需显式 move:std::move_only_function<int> f = std::move(existing_func);</int>,前提是existing_func为空或内部可移动(C++23 中部分实现允许空std::function安全 move)
传递和存储 move_only_function 的注意事项
由于它不可复制,所有涉及“传值”或“放入容器”的场景都必须用移动语义,否则编译器立刻报错。这是最容易踩坑的地方。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 函数参数尽量用右值引用:
void handle(std::move_only_function<void> &&f)</void>;若需保留所有权,应直接 move 进成员变量 - 存入
std::vector必须用emplace_back或push_back(std::move(f));直接push_back(f)会触发拷贝尝试而失败 - 不能作为
std::shared_ptr或std::weak_ptr的目标类型(它们要求被管理对象可拷贝/可赋值),但可以用std::unique_ptr<:move_only_function>></:move_only_function> - lambda 捕获列表里如果用了
=,且捕获了非拷贝类型,整个 lambda 就变成 move-only,此时必须用std::move_only_function接收,别指望编译器“自动降级”
和 std::function 的兼容性与性能差异
两者接口高度相似,但不兼容:不能相互赋值,也不能混用模板特化。性能上,std::move_only_function 在小对象场景下可能略快(省去拷贝检查和虚表拷贝逻辑),但堆分配开销类似。
- 目前主流标准库(libstdc++ 13+、libc++ 17+、MSVC 19.35+)已支持,但旧版本(如 GCC 12 或 Clang 15)可能只有实验性支持或缺失,需确认
__cpp_lib_move_only_function >= 202110L - 不支持 SBO(small buffer optimization)的实现可能导致每次构造都堆分配,而
std::function在多数实现中对小 lambda 有栈内优化 - 如果你的代码需要同时支持 C++20 和 C++23,不要试图用宏模拟
std::move_only_function——它没有合理的降级方案;要么分条件编译,要么坚持用std::function并避免 move-only 捕获
真正要注意的是:一旦你决定用 std::move_only_function,整条调用链就得跟着“移动到底”。中间任何一个环节忘了 std::move,或者试图取地址、拷贝、绑定到 const 引用再传出去,都会卡住编译。这不是语法糖,是所有权模型的硬约束。


















