C++11之后引入lambda表达式,,一般用于定义匿名函数,使得代码更加灵活简洁。lambda表达式与普通函数类似,也有参数列表、返回值类型和函数体,只是它的定义方式更简洁,并且可以在函数内部定义。
1 lambda表达式概述
最常见的lambda表达式如下:
1 | auto plus = [] (int v1, int v2) -> int { return v1 + v2; } |
上述代码是计算两数之和的,但是一般不会这样使用
2 lambda表达式的写法
| 序号 | 写法 |
|---|---|
| (1) | [captures]<tparams>(params)lambda-specifiers{body} |
| (2) | [captures](params)rtailing-return-type{body} |
| (3) | [captures](params){body} |
| (4) | [captures]lambda-specifiers{body} |
3 四种表达式的含义
(1)完整的lambda表达式,包含了lambda表达式的所有成分。
(2)常量lambda表达式,捕获的变量都是常量,不能在lambda表达式的body中进行修改。
(3)和(2)基本一致,唯一的区别就是,lambda表达式的函数返回值可以通过函数体推导出来。一般情况函数返回值类型明确或者没有返回值的情况下可以这样写。
(4)lambda表达式的函数没有任何参数,但是可以添加lambda-specifiers,lambda-specifiers是什么我们后续再介绍。
4 lambda表达式各个成员的解释
captures 捕获列表,lambda可以把上下文变量以值或引用的方式捕获,在body中直接使用。
tparams 模板参数列表(c++20引入),让lambda可以像模板函数一样被调用。
params 参数列表,有一点需要注意,在c++14之后允许使用auto左右参数类型。
lambda-specifiers lambda说明符, 一些可选的参数,这里不多介绍了,有兴趣的读者可以去官方文档上看。这里比较常用的参数就是mutable和exception。其中,表达式(1)中没有trailing-return-type,是因为包含在这一项里面的。
trailing-return-type 返回值类型,一般可以省略掉,由编译器来推导。
body 函数体,函数的具体逻辑。
5 捕获列表
上面介绍完了lambda表达式的各个成分,其实很多部分和正常的函数没什么区别,其中最大的一个不同点就是捕获列表。我在刚开始用lambda表达式的时候,还一直以为这个没啥用,只是用一个 [] 来标志着这是一个lambda表达式。后来了解了才知道,原来这个捕获列表如此强大,甚至我觉得捕获列表就是lambda表达式的灵魂。下面先介绍几种常用的捕获方式。
[] 什么也不捕获,无法lambda函数体使用任何
[=] 按值的方式捕获所有变量
[&] 按引用的方式捕获所有变量
[=, &a] 除了变量a之外,按值的方式捕获所有局部变量,变量a使用引用的方式来捕获。这里可以按引用捕获多个,例如 [=, &a, &b,&c]。这里注意,如果前面加了=,后面加的具体的参数必须以引用的方式来捕获,否则会报错。
[&, a] 除了变量a之外,按引用的方式捕获所有局部变量,变量a使用值的方式来捕获。这里后面的参数也可以多个,例如 [&, a, b, c]。这里注意,如果前面加了&,后面加的具体的参数必须以值的方式来捕获。
[a, &b] 以值的方式捕获a,引用的方式捕获b,也可以捕获多个。
[this] 在成员函数中,也可以直接捕获this指针,其实在成员函数中,[=]和[&]也会捕获this指针。
6 总结
捕获列表,对应LambdaClass类的private成员。
参数列表,对应LambdaClass类的成员函数的operator()的形参列表
mutable,对应 LambdaClass类成员函数 operator() 的const属性 ,但是只有在捕获列表捕获的参数不含有引用捕获的情况下才会生效,因为捕获列表只要包含引用捕获,那operator()函数就一定是非const函数。
返回类型,对应 LambdaClass类成员函数 operator() 的返回类型
函数体,对应 LambdaClass类成员函数 operator() 的函数体。
引用捕获和值捕获不同的一点就是,对应的成员是否为引用类型。
感谢看到这里,在记录中收获成长,道阻且长