std::function是类型擦除的可调用对象包装器,支持统一处理多种可调用实体但有运行时开销;声明需严格匹配签名,赋值需兼容参数、返回值及限定符;应避免在hot path滥用,优先move传递以避免深拷贝,调用前须用if(func)检查空状态。

std::function 用来替代函数指针和仿函数,但不是万能胶水
它本质是类型擦除的可调用对象包装器,能统一处理普通函数、lambda、成员函数、bind 表达式等,但会带来一小段运行时开销(内部通常用 heap allocation + vtable 调度)。别在 hot path 上无脑替换裸函数指针;如果只传普通函数且不捕获,void(*)(int) 仍更轻量。
声明和赋值必须严格匹配签名,否则编译失败
声明时要写明完整的调用签名,比如 std::function<int const std::string></int>。赋值对象的参数类型、返回类型、const/volatile 限定、是否 noexcept 都需兼容——但 C++ 允许隐式转换(如 int → double),只要目标签名能接受即可。
常见错误现象:error: no matching constructor for initialization of 'std::function<void>'</void>,往往是因为右边是个需要捕获变量的 lambda,而你声明的是无参无捕获版本;或者右边是成员函数指针,没用 std::bind 或 lambda 包一层。
- 普通函数:直接赋值,如
func = &my_free_func; - lambda:带捕获或不带捕获都行,如
func = [x](int a) { return a + x; }; - 成员函数:必须绑定对象,如
func = std::bind(&MyClass::method, obj, std::placeholders::_1);或更推荐func = [&obj](int a) { return obj.method(a); };
移动语义对性能很关键,尤其存储大量 lambda 时
std::function 内部可能堆分配,拷贝构造/赋值会触发深拷贝(比如复制整个捕获的栈帧)。若你频繁传递或存入容器(如 std::vector<:function>></:function>),应优先 move:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::vector<std::function<void()>> tasks;
auto f = [data = std::move(big_vector)]() mutable { /* ... */ };
tasks.push_back(std::move(f)); // ← 必须 move,否则 big_vector 被拷贝两次注意:一旦 move 出去,原 std::function 变成 empty 状态(func 调用会抛 std::bad_function_call),这点容易被忽略。
检查是否为空比 try-catch 更安全可靠
调用前不检查空状态就执行,会直接抛 std::bad_function_call 异常。这不是设计缺陷,而是明确要求使用者主动防御。相比异常路径,用 if (func) 判断更轻量、更可控。
使用场景举例:回调注册、事件分发、延迟执行队列。这些地方本就该允许“无回调”语义,而非靠异常兜底。
- 正确:
if (on_complete) on_complete(result); - 危险:
on_complete(result); // 万一没注册? - 更危险:用
try { ... } catch (const std::bad_function_call&) { ... }——异常开销大,且掩盖了逻辑疏漏
empty 检查本质是判断内部函数指针是否为 null,几乎零成本;而异常抛出涉及栈展开,在嵌入式或实时系统里尤其要避开。

















