c++ lambda 表达式语法为[捕获](参数)->返回类型{主体},捕获列表必写,参数括号不可省,返回类型可推导;常用于stl算法,需注意捕获方式与生命周期;每个lambda类型唯一,跨作用域传递宜用std::function。

lambda 表达式的基本语法结构
在 C++ 中,[capture](parameters) -> return_type { body } 是完整 lambda 的骨架。方括号 [] 是捕获列表,必须有;圆括号 () 内的参数可空(此时可省略 void),但括号不能省;箭头 -> 和返回类型可省略(编译器自动推导),但一旦函数体含多条语句或返回类型不明确,就容易推导失败;花括号内是执行逻辑。
常见写法示例:
auto f1 = []() { return 42; }; // 无参无捕获,返回 int
auto f2 = [&](int x) { vec.push_back(x); }; // 按引用捕获外部变量,接受一个 int
auto f3 = [val](double d) -> std::string { return std::to_string(val * d); }; // 值捕获 val,显式声明返回类型注意:若只捕获部分变量,必须显式列出;[=] 或 [&] 是全量捕获,但会隐式忽略 this(类成员函数中需写 [this] 或 [=, this] 才能访问成员)。
在 STL 算法中传入 lambda 的典型用法
lambda 最常用于 std::sort、std::find_if、std::transform 等算法的谓词参数。此时重点不是语法炫技,而是避免意外拷贝和生命周期问题。
立即学习“C++免费学习笔记(深入)”;
-
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });—— 安全,无捕获,纯函数式 -
std::find_if(data.begin(), data.end(), [target](const auto& item) { return item.id == target; });—— 值捕获target,确保 lambda 生命周期内target有效 - 错误写法:
[&target]若target是局部变量且 lambda 在函数返回后被调用(如存入容器、异步调度),就会引发悬垂引用
性能提示:简单比较逻辑尽量用值捕获或无捕获,避免引用捕获带来的 lifetime 约束;对大对象,考虑用 const& 参数而非值传递。
lambda 作为函数对象存储与转发的限制
每个 lambda 表达式生成一个**唯一的、未命名的闭包类型**,即使两个 lambda 写法完全一样,类型也不同。这意味着:
- 不能直接用
auto以外的类型名声明多个相同逻辑的 lambda 变量(auto是最安全选择) - 不能把 lambda 直接赋给
std::function以外的普通函数指针(除非它不捕获——此时可隐式转为函数指针) - 想存起来反复用或跨作用域传递,优先用
std::function包装,但要注意其有小对象优化(SBO)阈值,捕获内容过大时会有堆分配开销
例如:
std::function<bool(int)> pred = [](int x) { return x % 2 == 0; }; // OK
bool (*fp)(int) = [](int x) { return x > 0; }; // OK:无捕获,可转函数指针
bool (*bad_fp)(int) = [&x](int y) { return y > x; }; // 编译错误:有捕获,无法转函数指针在类成员函数中使用 lambda 访问 this 和成员变量
类内定义 lambda 时,默认不捕获 this,哪怕用了 [=] 或 [&]。这是 C++17 起明确规定的,目的是防止隐式延长对象生命周期。
正确做法只有两种:
- 显式写
[this]:捕获this指针,可读写所有非静态成员(包括私有) - 显式写
[=, this]或[&, this]:同时按值/引用捕获外部变量 + 显式捕获this
陷阱示例:
class Widget {
int value = 42;
void process() {
auto bad = [=]() { return value; }; // ❌ 编译错误:value 不在捕获列表中
auto good = [this]() { return value; }; // ✅ 正确
auto also_good = [=, this]() { return value + offset; }; // ✅ 同时捕获 this 和外部变量 offset
}
};<p>如果 lambda 需要异步执行(比如交给线程或定时器),务必确认对象生命周期是否覆盖 lambda 执行期;否则 <code>[this]</code> 会导致悬垂指针。</p>










