一、简介
C++11才开始具备lambda表达式这种特性
lambda表达式的基本语法:
[captures](params)specifiers exception -> ret {body}
eg:
int x = 5;
auto foo = [x](int y)->int {
return x*y;
}
cout<<foo(8)<<endl;
captures是捕获列表,它可以捕获当前函数所在作用域中零或多个变量
params是参数列表
specifiers是可选限定符,如mutable(允许在函数体内改变按值捕获的变量 或 调用非const类型修饰的成员函数)
exception是可选异常说明符,可以使用noexcept来指明表达式lambda是否会抛出异常
return 返回值类型
body函数体
二、lambda表达式捕获列表
1、捕获列表的作用域
捕获列表中的变量存在于两个作用域中,分别是定义lambda表达式的函数作用域,一个是lambda表达式函数体的作用域,所以标准规定捕获的变量必须是一个自动存储类型。
int x= 0;
int main()
{
int y = 0;
static int z = 0;
auto foo = [x, y, z] {};
return 0;
}
如x和z都不是自动存储类型的变量,所以无法编译通过,不过不同编译器的处理方式不同,比如GCC编译器只会发出警告
2、捕获值和捕获引用
int x = 5, y = 8;
auto foo1 = [x, y] {return x * y};
auto foo2 = [&x, &y] {return x * y};
3、特殊的捕获方法
[this]-捕获this指针,捕获this指针可以让我们使用this类型的成员变量和函数
[=]-捕获定义作用域的全部变量的值,包括this
[&]-捕获定义作用域的全部变量的引用,包括this
在C++20中,无状态的lambda表达式是可以隐式转换成函数指针的
void func(void(*)()) {}
void g() {
func([](){});//编译成功
}
这一点在需要函数回调为参数的函数中是比较有用的
在STL中应用lambda表达式是比较方便的
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> x = {1, 2, 3, 4, 5};
cout<<find_if(x.cbeign(), x.cend(), [](int i){
return (i%3) == 0;
})
}
例如sort和find_if,这些函数都是需要辅助函数或者辅助对象对象来帮助完成算法的,这种情况下使用lambda表达式可以让代码看起来非常的直接。
4、广义捕获
简单捕获:前面介绍的内容
初始化捕获:如下图
int main() {
int x = 5;
auto foo = [r = x + 1]() {
return r;
}
}
x所在的作用与是main函数,r所在的作用域是lambda表达式,这就是等号左右作用域的区别
初始化捕获的一大好处是可以方便的对捕获的对象进行处理
另外在C++14标准中还支持泛型lambda表达式,这里的泛型并没有使用模版,而是通过auto占位符来完成的。
int main() {
auto foo = [](auto a) {return a;};
foo(3);
foo("hello lambda!");
}
代码中的foo就是一个典型的泛型lambda表达式。
5、其他特性
1、捕获[*this]和捕获[=, this]
捕获*this的目标是拷贝this对象,并且在lambda表达式中使用拷贝的对象,这种用法常用语一些异步的函数中,而[=, this]是直接使用this对象与 [=]意思是相同的。
2、模版语法的泛型lambda表达式
C++20标准为了让泛型表达式使用起来更加方便,引入了模版语法的泛型lambda表达式,这就和函数模版没有区别了
auto f = []<typename T>(vector<T> vector) {
// ...
}
3、可构造和可赋值的无状态lambda表达式
最后C++20 的标准还允许构造和赋值无状态的lambda表达式
auto greater = [](auto x, auto y) {return x > y;};
map<string, int, decltype(greater)> mymap;
这里greater是一个无状态的lambda表达式,map模版的第3个形参需要一个比较函数的类型,在C++20之前使用decltype获取lambda表达式的类型并且作为模版实参传入map是不能编译通过的。因为map在使用这个参数时会构造出对象,而在C++20 之前lambda表达式类型是无法构造出对象的,C++20标准解决了这个问题,对于无状态的lambda表达式类型是可以构造和赋值的.
总结:
lambda表达式解决了过去C++中无法就地编写内嵌函数的尴尬问题,虽然GCC中提供了一种嵌套函数的C语言扩展,不过这个特性一直没有纳入标准之中。合理使用lambda表达式可以让我们的代码更加具有可读性。