Lambda可作为函数参数传递,但必须用std::function或模板参数接收;无捕获的才能隐式转函数指针,带捕获的只能用std::function(类型擦除、签名匹配)或模板(零开销、编译期推导)。

能传,但方式选错会编译失败或隐式拷贝开销大;最稳妥的是用 std::function,性能敏感场景可考虑模板参数。
用 std::function 接收 Lambda 最通用
这是最直接、兼容性最好的方式,适用于所有可调用对象(Lambda、函数指针、functor)。它把类型擦除,只关心签名是否匹配。
- 必须包含头文件
<functional> - 声明时需明确写出调用签名,例如
std::function<bool int></bool>,不能只写std::function<auto> - 每次传参都会发生一次小对象拷贝(内部有约 24 字节的存储缓冲),对极高频调用(如每帧上万次)可能有影响
- 支持捕获变量的 Lambda(包括
[=]、[&]、[this]),无需额外处理
示例:
#include <vector>
#include <functional>
#include <algorithm>
void process(std::vector<int>& v, std::function<bool(int, int)> cmp) {
std::sort(v.begin(), v.end(), cmp);
}
// 调用
std::vector<int> data = {3, 1, 4};
process(data, [](int a, int b) { return a < b; });
用模板参数接收 Lambda 避免运行时开销
当 Lambda 是临时、一次性使用的(比如算法回调),模板推导能完全避免 std::function 的类型擦除成本,且支持 constexpr 场景。
立即学习“C++免费学习笔记(深入)”;
- 函数定义需是模板:例如
template <typename Compare> void process(..., Compare&& cmp) - 完美转发推荐用右值引用
Compare&&+std::forward<Compare>(cmp),避免不必要的拷贝 - 不支持直接传入函数指针(除非显式转换),也不方便存为成员变量或跨函数传递
- 如果 Lambda 捕获了局部变量,而你试图把它存进类成员再 later 调用,模板方式会失败——因为类型是 unique 的,无法命名和保存
示例:
template <typename Compare>
void process_opt(std::vector<int>& v, Compare&& cmp) {
std::sort(v.begin(), v.end(), std::forward<Compare>(cmp));
}
process_opt(data, [](int a, int b) { return a > b; }); // 升序变降序,零开销
传带捕获的 Lambda 时要注意生命周期
捕获列表决定 Lambda 是否安全:按引用捕获([&x] 或 [&])的变量一旦离开作用域,Lambda 再调用就会 UB。
- 若 Lambda 可能被保存、异步执行或延迟调用,一律改用值捕获(
[x]或[=]) -
[=]捕获的是副本,但如果捕获的是指针或智能指针,仍要确保所指对象生命周期足够长 - 类成员函数中写 Lambda 并捕获
this,要确认该 Lambda 不会在对象析构后被调用 - 不要在返回的 Lambda 中捕获局部数组或栈变量的引用
为什么不能直接用函数指针类型?
只有无捕获的 Lambda(即 [](...) { ... })才能隐式转为函数指针;一旦用了 [x]、[&] 或 [this],就不再是“普通函数”,没有对应函数指针类型。
- 错误写法:
void f(bool(*)(int, int))→ 传[x](int a, int b){...}会编译失败 - 即使编译通过(比如传了个空捕获 Lambda),后续若想扩展逻辑加捕获,就得改接口,不灵活
- 函数指针无法体现调用约定(如
__stdcall),跨平台也更脆弱
真正需要函数指针的极少数场景(如 C API 回调),得先用 std::function 封装,再借助 std::bind 或静态桥接函数中转——但这已不属于“直接传 Lambda”的范畴。


















