std::move_only_function 是 C++23 引入的仅支持移动语义的类型擦除可调用包装器,区别于 std::function 的关键在于删除拷贝构造/赋值,可封装 unique_ptr 捕获 lambda 等 move-only 对象,而 std::function 要求可拷贝。

std::move_only_function 是什么,和 std::function 有什么区别
std::move_only_function 是 C++23 引入的类型擦除可调用对象包装器,核心定位是「仅支持移动、不支持拷贝」。它和 std::function 的最大区别在于:前者删除了拷贝构造函数和拷贝赋值运算符,只保留移动语义;后者要求其内部存储的可调用对象必须满足 CopyConstructible(比如 lambda 带捕获时若未显式声明为 mutable 且捕获了非 const 对象,可能无法满足)。
这意味着:std::move_only_function 能封装那些天然不可拷贝的对象,比如含 std::unique_ptr 捕获的 lambda、临时 std::thread、或自定义的 move-only functor。而 std::function 在这些场景下编译失败,报错类似 use of deleted function 'X::X(const X&)'。
怎么声明和初始化 std::move_only_function
声明方式与 std::function 类似,但模板参数只接受签名(返回类型 + 参数列表),不支持默认模板参数推导,必须显式写出:
std::move_only_function<void()> f1 = []() { /* ... */ };
std::move_only_function<int(int, const std::string&)> f2 = [](int a, const std::string& s) { return a + s.size(); };注意以下几点:
立即学习“C++免费学习笔记(深入)”;
- 不能用
{}初始化列表直接构造(如std::move_only_function<void> f{[](){}};</void>会触发隐式转换,但某些标准库实现(如 libstdc++ 13.2)仍有 bug,建议统一用=复制初始化 - 绑定右值 lambda 或临时对象时,必须确保生命周期足够长——
std::move_only_function会移动捕获,但不会延长被移动对象的生命周期 - 不能从 const lvalue 初始化:例如
auto lam = [](){}; std::move_only_function<void> f = lam;</void>❌ 编译失败,因为lam是 const lvalue,而move_only_function构造函数只接受可移动的 rvalue 或支持移动的 lvalue(需显式std::move(lam))
std::move_only_function 的移动行为和常见陷阱
移动后原对象处于有效但未指定状态(valid but unspecified state),**不能再次调用 operator()**,也不能再移动——多数实现会将其内部函数指针置空,调用时抛出 std::bad_function_call。
典型误用场景:
- 移动后仍尝试调用:
f();→ 运行时报错std::bad_function_call - 多次移动同一对象:
auto f2 = std::move(f1); auto f3 = std::move(f1);→ 第二次移动未定义行为(f1 已失效) - 在容器中使用时忽略移动语义:
std::vector<:move_only_function>> v; v.push_back(std::move(f));</:move_only_function>✅ 正确;但v.emplace_back(f);❌ 错误,因f是 lvalue,需写v.emplace_back(std::move(f))
性能上,std::move_only_function 通常比 std::function 更轻量:省去拷贝检查开销,小对象优化(small buffer optimization)策略也可能更激进,但具体取决于标准库实现。
何时该选 std::move_only_function 而不是 std::function
优先考虑 std::move_only_function 的真实场景:
- 回调注册接口明确只消费一次(如异步任务提交、事件一次性监听器):避免用户意外拷贝导致资源重复释放
- 封装含
std::unique_ptr、std::ofstream等 move-only 成员的 lambda:例如[ptr = std::make_unique<int>(42)]() mutable { *ptr = 0; }</int>只能用move_only_function - 泛型代码中需要统一处理 move-only 和 copyable 可调用对象,且已确定无需拷贝语义
- 你正在写一个 move-only 类型的类,其成员函数对象字段必须保持整体 move-only 性质
反例:需要把同一个回调反复传给多个地方(如 GUI 事件处理器、日志钩子),这时仍得用 std::function,或改用共享所有权(std::shared_ptr 包裹)加 std::function。
目前主流标准库支持情况:MSVC 19.35+、Clang 16+(配合 libc++ 16)、GCC 13+(libstdc++ 13.2 起)。使用前务必检查 __cpp_lib_move_only_function 宏。


















