c++oding="utf-8" ?>
std::move_only_function是专为禁止拷贝场景设计的独立类型,仅要求MoveConstructible;捕获unique_ptr的lambda因禁用拷贝构造,只能赋值给它,而std::function要求CopyConstructible故编译失败。

std::move_only_function 不是 std::function 的轻量版替代品,而是专为“必须移动、禁止拷贝”的场景设计的独立类型;用错地方会立刻编译失败,没有运行时妥协余地。
为什么 std::function 编译不过,而 std::move_only_function 可以?
当你写一个捕获 std::unique_ptr 的 lambda,它自动禁用拷贝构造——std::function 要求目标满足 CopyConstructible,直接拒绝编译;std::move_only_function 只要求 MoveConstructible,绕过该约束。
典型错误信息:error: use of deleted function 'X::X(const X&)',这不是你的代码写错,是语义不匹配。
- lambda 捕获
std::unique_ptr、std::mutex或自定义删除了拷贝构造的类 → 只能进std::move_only_function - 普通无捕获或仅值捕获的 lambda 默认可拷贝 → 不能直接赋给
std::move_only_function,必须显式std::move构造(否则编译器认为你在尝试拷贝) - 捕获引用(如
[&x])本身不阻止拷贝,但 move 后原闭包仍持有悬空引用,运行时崩
怎么正确声明和初始化 std::move_only_function?
模板参数必须显式写出完整调用签名,含 noexcept、&、const 等修饰;没有 make_move_only_function 辅助函数,也不能靠 auto 推导。
例如:std::move_only_function<int int const noexcept></int> 表示一个 const、noexcept 的二元函数对象;漏掉 const 可能在 const 成员函数里调用时报错。
- ✅ 正确:
std::move_only_function<void> task = [ptr = std::make_unique<int>(42)]() mutable { std::cout </int></void> - ❌ 错误:
std::move_only_function<void> task = []() { }; </void>(无捕获 lambda 可拷贝,但std::move_only_function不接受隐式拷贝构造) - ✅ 补救:
std::move_only_function<void> task{[]() { }};</void>或std::move_only_function<void> task = std::move([]() { });</void>
往 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_only_function成员 - 别把它塞进需要
CopyConstructible的老接口(如某些第三方回调注册函数),编译不过就是不过,没商量余地
它真的只是“更小一点内存”吗?
空 std::move_only_function 通常占 16 字节(两个指针),std::function 常为 32 字节;但这只是表象——关键在于它彻底省掉了拷贝逻辑所需的虚函数表项、引用计数、SBO 对齐填充和析构守卫。
小 callable(如无捕获 lambda)可能原地构造,不触发堆分配;但一旦超过小缓冲区容量(libc++ 默认 24 字节,libstdc++ 通常 32 字节),仍会堆分配。移动构造开销接近 memcpy。
最易被忽略的一点:它不提供 target() 或 target_type(),因为 move-only 类型擦除后无法安全还原原始类型;如果你依赖类型反射,这个限制是硬性的,不是暂时缺失。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











