std::move_only_function是C++23引入的仅移动函数封装器,专用于不可拷贝的可调用对象,如捕获unique_ptr或mutex的lambda、禁用拷贝的functor及返回move-only类型的回调。

std::move_only_function 是 C++23 引入的轻量级、仅移动(move-only)函数封装器,专为不能拷贝的可调用对象设计。它不依赖 std::function 的拷贝语义和类型擦除开销,底层不强制要求目标可调用对象支持拷贝——只要能移动即可。
什么时候必须用 std::move_only_function 而不是 std::function
当你需要存储或传递以下类型的可调用对象时,std::function 会编译失败,而 std::move_only_function 是唯一可行选择:
- 捕获了
std::unique_ptr、std::mutex或其他不可拷贝资源的 lambda - 移动语义明确禁用拷贝的自定义 functor(例如声明了
deleted 拷贝构造/赋值) - 返回
std::unique_ptr或其他 move-only 类型的异步回调(如某些协程 awaiter 回调)
典型错误现象:error: use of deleted function 'X::X(const X&)' —— 这说明你试图把 move-only 可调用对象塞进 std::function。
std::move_only_function 的声明与基本用法
头文件是 ,模板参数语法与 std::function 一致,但内部不提供拷贝构造/赋值操作符:
立即学习“C++免费学习笔记(深入)”;
std::move_only_functionf1; f1 = [ptr = std::make_unique (42)](int x, const std::string& s) mutable { return x + *ptr + s.size(); }; // OK:lambda 含 unique_ptr,仅可移动 // f1 = f1; // 编译错误:no matching constructor for initialization
注意点:
- 不能隐式转换为
std::function,也不能从std::function构造 - 支持
nullptr初始化,调用前需用if (f)检查是否为空 - 不保证 noexcept,调用 operator() 仍可能抛异常(取决于被封装对象)
性能与内存布局差异:为什么它更轻?
std::move_only_function 不强制要求 small-buffer optimization(SBO),也不预留固定大小的缓冲区来“内联”小可调用对象。它的实现更接近裸指针+虚表或状态机式分发,避免了 std::function 中为兼容拷贝而做的冗余存储和分支判断。
- 典型大小:在 libstdc++ 和 libc++ 中,空
std::move_only_function通常为 16 字节(两个指针),而std::function常为 32 字节(含 SBO 缓冲) - 无拷贝意味着无引用计数、无原子操作、无额外析构守卫逻辑,移动构造/赋值更快
- 不支持
target或target_type()—— 类型信息在移动后即丢失,这是设计取舍
常见陷阱与兼容性提醒
目前(GCC 13/Clang 17,MSVC 19.37+)支持尚属早期,容易踩坑:
- libstdc++ 需要
-std=c++23 -D_GLIBCXX_USE_CXX11_ABI=1,否则可能链接失败或行为异常 - 不能用于需要拷贝的上下文:例如
std::vector<:move_only_function>>的resize()或insert()会触发拷贝,应改用emplace_back()或确保只 move - 不能绑定到
std::bind返回值(因std::bind结果默认可拷贝),需改用 lambda 封装 - 调试时无法像
std::function那样通过target_type()查看类型 —— 这是 move-only 设计的必然代价
真正关键的限制在于:它只解决「移动」问题,不解决「类型反射」或「运行时检查」需求。一旦你需要知道封装的是什么,就得在封装前自己记下来。










