C++ Lambda表达式

本文详细介绍了C++11中的Lambda表达式,包括写法、不同形式的省略、命名Lambda的使用、捕获变量列表(值捕获与引用捕获)、Lambda在STL中的应用。通过实例演示了如何定义和执行Lambda,以及捕获外部变量的方式。

参考:
C++ 11 Lambda表达式
C++之Lambda表达式

1、Lambda表达式写法格式

[捕获变量列表] (输入参数列表) mutable 或 exception 声明 -> 返回值类型 {函数体}

几种省略写法:

[] (输入参数列表)-> 返回值类型 {函数体} // 捕获变量列表为空
[] (输入参数列表){函数体} // 自动推导返回值类型,没有return语句时返回类型为void
[]{函数体} // 捕获列表、输入参数均为空

2、Lambda的名称与执行

2.1为Lambda指定名称并执行

auto lambda_name = [](int a) { std::cout << a; };
lambda_name(1); // 执行Lambda
lambda_name(2);

lambda_name 为该Lambda的名称,相当于函数名。lambda_name 的实际类型随表达式的实现而定,取决于编译器使用是, 类型来跟踪Lambda。可以像使用常规函数那样使用有名称的Lambda。

2.2定义的同时执行Lambda

也可以在定义Lambda的同时执行它:

[](int a) { std::cout << a; }(1);

后面的(1)为实参列表。此时该Lambda并没有名称。

2.3不能在直接执行时给Lambda取名

auto lambda_name = [](int a) { std::cout << a; }(1); // 编译失败,error:“lambda_name”: 变量不能具有类型“void”

因为在直接执行Lambda时,前面的lambda_name会被认为是一个变量,用于接收Lambda的返回值,而非Lambda的名称。但此处Lambda的返回类型为void,变量不能为void类型,因此报错。
如果Lambda有返回值,则可定义变量来接收返回值:

auto m = [](int a) { return ++a; }(1); // 编译通过,m=2
auto n = [](int x) { return [](int y) { return y * 2; }(x)+6; }(5); // 编译通过,n=16

3、捕获变量列表

捕获变量列表用于列写需要捕获的外部变量名称。Lambda可以捕获其可见范围内的外部变量,在捕获列表[]中列写后即可在Lambda内部使用这些变量。

捕获的方式分为两种:值捕获 和 引用捕获

3.1值捕获

int a = 1;
auto lambda_name = [a]() { std::cout << a; };
lambda_name();

直接在[]中列写需要捕获的外部变量名称,类似于函数参数的值传递。拷贝的参数在函数内部是只读的,Lambda内部不能改变捕获得到的变量值。加上mutable关键字可以改变拷贝值,但不会影响原值:

int a = 1;
// auto lambda_name = [a](){ std::cout << ++a; }; // 编译失败,不能改变值捕获的变量值
auto lambda_name = [a]()mutable { std::cout << ++a; }; //编译成功,但不改变变量a的原始值
lambda_name();
std::cout << a; // a=1

也可以用=符号值捕获所有可见范围内的变量:

{
	int x = 10;
}
int a = 1;
int b = 2;
// auto lambda_name = [=]() { std::cout << x; }; // 编译失败,x不可见
auto lambda_name = [=]() { std::cout << a + b; }; // 编译成功,a、b可见
lambda_name();

3.2引用捕获

int a = 1;
auto lambda_name = [&a]() { std::cout << ++a; };
lambda_name();
std::cout << a; // a=2

在捕获的外部变量名称前面加&,类似于函数参数的引用传递。Lambda不需要mutable关键字就允许改变捕获得到的变量值,并且会改变该变量的原始值。
也可以用&符号引用捕获所有可见范围内的变量:

{
	int x = 10;
}
int a = 1;
int b = 2;
// auto lambda_name = [&]() { std::cout << x; }; // 编译失败,x不可见
auto lambda_name = [&]() { std::cout << (++a) + (++b); }; // 编译成功,a、b可见
lambda_name();
std::cout << a << b;

3.3混合捕获

值捕获与引用捕获可以混合使用:

int a = 1;
int b = 2;
auto lambda_name = [&a, b]() mutable { std::cout << (++a) + (++b); }; // 显示5
lambda_name();
std::cout << a << b; // a=2,b=2

或者:

int a = 1;
int b = 2;
int c = 3;
// auto lambda_name = [&a,=]() mutable { std::cout << (++a) + (++b) + (++c); }; // 编译失败,error:"=": 捕获默认只能出现在 lambda 捕获列表的开头
// auto lambda_name = [a,&]() mutable { std::cout << (++a) + (++b) + (++c); }; // 编译失败,error:"&": 捕获默认只能出现在 lambda 捕获列表的开头
auto lambda_name = [=,&a]() mutable { std::cout << (++a) + (++b) + (++c); }; // 编译成功,显示9
lambda_name();
std::cout << a << b << c; // a=2,b=2,c=3

除了变量a为引用捕获,其他变量均为值捕获。注意:=必须写在&a的前面。

4、Lambda用于STL

在C++11中,对于接受函数指针或函数对象的函数,可以使用Lambda作为其参数。

std::vector<int> buf = {1,4,3,5,2};
std::sort(buf.begin(), buf.end(), [](const int a, const int b) {return a < b; });
for (int x : buf) std::cout << x;

Lambda在此处代替了一般使用的函数指针,使代码更加简洁。

### C++ Lambda 表达式用法详解 Lambda 表达式C++11 引入的重要特性之一,它允许开发者在调用或传递函数对象时直接定义匿名函数,极大提升了代码的简洁性和可读性。Lambda 表达式广泛应用于 STL 算法、异步编程、事件回调等场景中,是现代 C++ 编程的核心工具之一。 #### 基本语法结构 一个完整的 Lambda 表达式通常由以下几个部分组成: - **捕获列表**(Capture List):指定 Lambda 表达式如何访问外部变量。 - **参数列表**(Parameter List):可选,用于指定 Lambda 接受的参数。 - **可变说明符**(mutable):允许修改按值捕获的变量副本。 - **异常说明符**(Exception Specifier):可选,指定 Lambda 是否抛出异常。 - **返回类型**(Return Type):可选,若未指定,编译器会自动推导。 - **函数体**(Function Body):包含具体的实现逻辑。 示例代码如下: ```cpp auto lambda = [](int a) -> int { return a + 1; }; std::cout << lambda(1) << std::endl; // 输出: 2 ``` #### 捕获机制 Lambda 表达式的捕获机制决定了它如何访问和修改外部变量,主要有以下几种方式: - **按值捕获**([=]):捕获外部变量的副本,Lambda 内部无法直接修改外部变量。若需修改副本,需使用 `mutable` 关键字。 - **按引用捕获**([&]):捕获外部变量的引用,Lambda 内部可直接修改外部变量。 - **显式捕获**:可精确指定捕获的变量及方式,例如 `[x, &y]` 表示按值捕获 `x`,按引用捕获 `y`。 示例代码如下: ```cpp int x = 10; auto lambda = [=]() mutable { x++; std::cout << x << std::endl; // 输出 11 }; lambda(); std::cout << x << std::endl; // 输出 10(外部 x 未变) ``` #### 生命周期管理 使用 Lambda 表达式时,尤其需要注意按引用捕获带来的生命周期问题。如果 Lambda 被存储并在捕获变量的生命周期结束后调用,可能导致未定义行为。因此,开发者需确保引用的变量在 Lambda 调用时仍然有效。 #### 性能考量 按值捕获大对象可能会带来拷贝开销,建议使用按引用捕获或智能指针来避免不必要的性能损耗。此外,隐式捕获(如 [=] 和 [&])仅捕获 Lambda 体内实际使用的外部变量,因此可以有效减少捕获范围。 #### 应用场景 Lambda 表达式在 STL 算法中表现尤为突出,例如 `std::for_each`、`std::transform` 等算法中可以使用 Lambda 来简化函数对象的定义。此外,Lambda 还广泛应用于异步编程、事件回调、闭包管理等场景中。 示例代码如下: ```cpp #include <algorithm> #include <vector> #include <iostream> int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; std::for_each(vec.begin(), vec.end(), [](int x) { std::cout << x << " "; }); std::cout << std::endl; return 0; } ``` #### 标准演进与未来趋势 随着 C++ 标准的不断演进,Lambda 表达式的功能也在不断增强。例如,C++20 引入了模式匹配(Pattern Matching),进一步扩展了 Lambda 的应用场景。未来,Lambda 表达式将继续成为现代 C++ 开发的核心工具之一。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值