c++23 std::move_only_function怎么用 c++只能移动的函数包装器【详解】

22次阅读

std::move_only_functionc++23 引入的仅支持移动语义的函数包装器,用于封装 unique_ptr、mutex 等不可拷贝的可调用对象,满足 MoveConstructible/MoveAssignable 要求,不支持拷贝和 target 相关反射接口

c++23 std::move_only_function怎么用 c++只能移动的函数包装器【详解】

std::move_only_function 是 C++23 引入的新特性,用来替代传统 std::function 中“仅支持可复制”这一限制,专为**只可移动(move-only)的可调用对象**设计。它不强制要求底层可调用对象支持拷贝,因此能包装 Lambda 捕获了 unique_ptr、fstream、mutex 或其他不可拷贝资源的闭包,也能包装 std::unique_ptr<:any_callable> 等 move-only 类型。

为什么需要 move_only_function?

std::function 要求其模板参数 F 必须满足 CopyConstructible。这意味着:如果你写了一个捕获了 std::unique_ptr 的 lambda,它本身不可拷贝,就无法存入 std::function —— 编译直接失败。

std::move_only_function 只要求 MoveConstructibleMoveAssignable,彻底解除了拷贝约束,让 move-only 逻辑可以自然封装和传递。

基本用法与声明方式

它的模板形参和调用签名声明方式与 std::function 高度一致:

立即学习C++免费学习笔记(深入)”;

  • 声明格式:std::move_only_function,例如 std::move_only_function
  • 可由任何满足调用签名且可移动的可调用对象构造:lambda(含 move-only 捕获)、函数指针、move-only functor、std::unique_ptr
  • 不提供 .target().target_type() 等反射接口(因 move-only 存储通常需类型擦除 + 动态分配,元信息难保全)

典型使用场景示例

以下代码展示如何包装一个带 unique_ptr 捕获的 lambda:

auto make_move_only_task() {     auto ptr = std::make_unique(42);     // 此 lambda 不可拷贝(unique_ptr 不可拷贝)     return std::move_only_function{[ptr = std::move(ptr)]() mutable {         return *ptr;     }}; }  int main() {     auto f = make_move_only_task();  // OK: 移动构造     // auto f2 = f;                  // ❌ 编译错误:不可拷贝     auto f3 = std::move(f);         // ✅ 合法:可移动     std::cout << f3() << "n";      // 输出 42 }

再比如包装一个临时的 std::unique_ptr<:function>> 或自定义 move-only 函数对象,也都适用。

注意事项与限制

  • 不能隐式转换为函数指针(即使目标是普通函数),因为 move-only 语义与函数指针的无状态本质冲突
  • 空状态检查仍用 if (f),但调用前务必判空(和 std::function 一致)
  • 性能上:内部通常依赖分配(类似 std::function),但实现可优化(如 small-buffer 优化对 move-only 类型更友好)
  • 不兼容旧标准:C++20 及更早无此类型;需编译器支持(GCC 13+、Clang 16+、MSVC 19.35+)并启用 -std=c++23

它不是 std::function 的“升级版”,而是互补角色:需要拷贝时用 std::function,只需移动时用 move_only_function —— 更精准表达意图,也更安全。

text=ZqhQzanResources