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; |
| |
| |
| auto lambda1 = [a, &b]() { return a + b; }; |
| |
| |
| auto lambda2 = [&]() { a++; b++; }; |
| |
| |
| auto lambda3 = [c = a * 2]() { return c; }; |
# 参数列表(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++; |
| }; |
| |
| increment(); |
# 返回类型(Return Type)
- 返回类型可省略,编译器根据
return
语句自动推导。
- 当函数体包含多个
return
语句且类型不一致时,必须显式指定返回类型。
示例
| |
| auto add = [](int x, int y) { return x + y; }; |
| |
| |
| 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
本质上是编译器生成的匿名类(闭包类型)的实例。
- 捕获的变量会成为该匿名类的成员变量。
- 等价转换示例
| |
| 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 算法等 |