不一定,但绝大多数常见场景下会拷贝;std::function构造时通过类型擦除进行值语义拷贝,即使空lambda或小对象也可能拷贝(soo仅省堆分配,不省拷贝),bind更甚,推荐用lambda替代。

std::function 构造时一定会拷贝可调用对象吗?
不一定,但绝大多数常见场景下会。std::function 的模板构造函数是泛型的,它内部会对传入的可调用对象(比如 lambda、函数指针、bind 表达式)做一次类型擦除——这个过程通常涉及一次内存分配(堆上)和一次完整拷贝。哪怕你传的是一个空捕获的 lambda,std::function 也不会直接存栈上,而是按标准要求“拥有”该对象的一份副本。
- 捕获了局部变量的 lambda:必然拷贝整个捕获列表(含值捕获的
std::string、std::vector等) -
std::bind表达式:不仅拷贝绑定的目标函数,还逐个拷贝所有绑定参数(包括右值也会被移动或拷贝进内部存储) - 函数指针或无状态 lambda:部分实现(如 libstdc++)可能做小对象优化(SOO),避免堆分配,但仍是值语义拷贝,不是引用
例如:
auto f = std::bind(func, x, y); // x、y 被拷贝进 bind 对象内部 std::function<void()> g = f; // f 再次被拷贝进 g 的内部存储
std::bind 返回的对象本身有开销吗?
有,而且是双重开销:一次在 std::bind 调用时,一次在赋给 std::function 时。
std::bind 返回的是一个未具名的函数对象类型,其大小取决于绑定参数的数量和类型。每个参数都会被完美转发并存储一份——即使你绑定的是一个 int,它也占 4 字节;绑定一个 std::string,就是一次深拷贝(除非移动)。
- 绑定右值临时量(如
std::bind(f, get_string())):触发移动构造,但仍有构造开销 - 绑定左值引用(如
std::bind(f, std::ref(x))):只存引用,但std::reference_wrapper本身仍需拷贝(轻量,但非零成本) - 和 lambda 相比:
[x,y]{ f(x,y); }在捕获时就完成拷贝,而std::bind多一层包装,且无法被内联(多数编译器对 bind 结果不内联)
怎么避免不必要的拷贝?
核心思路是:别让 std::function 或 std::bind 成为默认选择;优先用更轻量、更明确的机制。
立即学习“C++免费学习笔记(深入)”;
- 用 lambda 替代
std::bind:现代 C++ 中几乎总是更优,编译器更容易优化,且捕获语义清晰std::function<void()> g = [x,y]{ func(x, y); }; - 避免把
std::bind结果再塞进std::function:直接用 auto 推导auto f = std::bind(func, x, y); // 类型固定,无类型擦除开销
- 如果必须用
std::function,且可调用对象较大,考虑用std::shared_ptr包裹后捕获auto shared_f = std::make_shared<decltype(f)>(std::move(f)); std::function<void()> g = [shared_f]{ (*shared_f)(); }; - 对性能敏感路径(如 hot loop 内部),干脆不用
std::function,改用模板参数或函数指针
小对象优化(SOO)真的能帮你省掉堆分配吗?
不能完全依赖。SOO 是实现细节,不同标准库表现差异大:
- libstdc++(GCC):对最多约 16 字节的小对象(如两个
int+ 函数指针)可能避免堆分配,但拷贝仍在 - MSVC STL:SOO 阈值更高(约 24–32 字节),但 bind 对象往往超限
- libc++(Clang):SOO 行为更保守,且对
std::bind结果支持较弱
关键点在于:SOO 只影响“是否 malloc”,不影响“是否拷贝”。即使走栈上存储,std::function 构造时仍执行完整对象复制——只是没调 malloc 而已。
真正容易被忽略的是:当你在 vector 里存一堆 std::function,每次 push_back 都可能触发拷贝 +(可能的)堆分配,而你根本没意识到那几个字节的 lambda 已经悄悄变成 heap churn。










