Lambda
表达式是 C++11
引入的一种匿名函数,允许在需要函数对象的地方直接定义一个函数,无需显式定义函数名。Lambda
表达式常用于实现回调函数、临时函数、算法函数等场景。
基本语法
Lambda 表达式的完整语法结构如下:
[ captures ] ( params ) mutable -> return_type { body }
组成部分 |
说明 |
[ captures ] |
捕获列表,定义外部变量如何被 Lambda 访问(值捕获、引用捕获等) |
( params ) |
参数列表,与普通函数参数列表一致 |
mutable |
可选关键字,允许修改按值捕获的变量 |
-> return_type |
可选返回类型,可省略(编译器自动推导) |
{ body } |
Lambda 函数体 |
捕获列表(Capture List)
捕获方式
捕获方式 |
语法示例 |
行为 |
按值捕获 |
[x] |
复制外部变量 x 的值到 Lambda 内部 |
按引用捕获 |
[&x] |
直接引用外部变量 x |
隐式按值捕获全部 |
[=] |
按值捕获所有外部变量(不推荐,易导致悬空引用或性能问题) |
隐式按引用捕获全部 |
[&] |
按引用捕获所有外部变量(不推荐,需谨慎管理生命周期) |
混合捕获 |
[x, &y] |
按值捕获 x ,按引用捕获 y |
初始化捕获(C++14+) |
[z = x + 1] |
创建新变量 z ,其值为 x + 1 (可用于移动语义或复杂初始化) |
示例代码
int a = 1, b = 2;
// 按值捕获 a,按引用捕获 b
auto lambda1 = [a, &b]() { return a + b; };
// 隐式按引用捕获所有外部变量
auto lambda2 = [&]() { a++; b++; };
// 初始化捕获(C++14+)
auto lambda3 = [c = a * 2]() { return c; }; // c = 2
参数列表(Parameters)
- 与普通函数参数列表一致,支持值传递、引用传递、默认参数等。
- 无参数时可省略:
[] { ... }
示例
auto add = [](int x, int y) { return x + y; }; // 值传递
auto print = [](const std::string& s) { std::cout << s; }; // 引用传递
auto no_args = [] { return 42; }; // 无参数
mutable 关键字
- 默认情况下,按值捕获的变量在
Lambda
内部是 不可修改 的。
- 使用
mutable
后,可以修改按值捕获的变量(修改的是副本,不影响外部变量)。
示例
int count = 0;
auto increment = [count]() mutable {
count++; // 允许修改按值捕获的 count(副本)
};
increment(); // 外部 count 仍为 0
返回类型(Return Type)
- 返回类型可省略,编译器根据
return
语句自动推导。
- 当函数体包含多个
return
语句且类型不一致时,必须显式指定返回类型。
示例
// 自动推导返回类型为 int
auto add = [](int x, int y) { return x + y; };
// 显式指定返回类型为 double
auto divide = [](int x, int y) -> double {
if (y == 0) return 0.0;
return static_cast<double>(x) / y;
};
Lambda 的应用场景
作为函数对象(Functor)
std::vector<int> nums = {1, 2, 3, 4};
std::sort(nums.begin(), nums.end(), [](int a, int b) {
return a > b; // 降序排序
});
异步回调(如 Boost.Asio)
socket.async_read_some(buffer, [this](const error_code& ec, size_t bytes) {
if (!ec) handle_read(bytes);
});
封装局部逻辑
void process_data(const std::vector<int>& data) {
int threshold = 10;
auto filter = [threshold](int x) { return x > threshold; };
std::copy_if(data.begin(), data.end(), std::back_inserter(result), filter);
}
注意事项
生命周期管理
- 按引用捕获:确保被引用的对象在
Lambda
执行时仍然有效,避免悬空引用。
- 按值捕获指针:需谨慎,可能造成内存泄漏或悬空指针。
性能
- 小对象按值捕获更高效,大对象(如容器)建议按引用捕获(需确保生命周期)。
默认捕获的风险
- 避免使用 [=] 或 [&]:可能导致意外捕获不需要的变量,增加维护难度。
Lambda
与函数对象的关系
Lambda
本质上是编译器生成的匿名类(闭包类型)的实例。
- 捕获的变量会成为该匿名类的成员变量。
- 等价转换示例
// Lambda 表达式
auto lambda = [x](int y) { return x + y; };
// 编译器生成的等价类
class __AnonymousClosure {
private:
int x;
public:
__AnonymousClosure(int x) : x(x) {}
int operator()(int y) const { return x + y; }
};
总结
特性 |
关键点 |
捕获列表 |
明确指定需要捕获的变量,避免隐式捕获 |
参数列表 |
与普通函数一致,支持多种传递方式 |
mutable |
允许修改按值捕获的变量副本 |
返回类型 |
可省略(自动推导)或显式指定 |
生命周期 |
谨慎管理按引用捕获的变量,避免悬空引用 |
应用场景 |
函数对象、异步回调、STL 算法等 |