基础语法和概念
lambda表达式定义了一个匿名函数
,并且可以捕获一定范围内的变量。
lambda
表达式在C++中会被看做是一个仿函数
,因此在使用lambda
返回值时要加 ()
进行调用。
lambda 表达式的语法形式简单归纳如下:
[capture](params) opt -> ret {body;};
其中 capture
是捕获列表,params
是参数列表,opt
是函数选项, ret
是返回值类型,body
是函数体。
- 捕获列表
[]
:捕获一定范围内的变量 - 参数列表
()
: 和普通函数的参数列表一样
auto f = [](){return 1;}; // 没有参数, 参数列表为空
auto f = []{return 1;}; // 没有参数, 参数列表省略不写
- opt 选项, 可省略
mutable
:可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身),因为按值传递的变量是只读状态,mutable 去掉按值捕获的外部变量的只读(const)属性,使其变得可以修改,而引用传递是可以直接修改值本身,即全局修改。
exception
:指定函数抛出的异常,如抛出整数类型的异常,可以使用throw();
constexpr
:C++17 ,可以指定 lambda 表达式是⼀个常量函数; - 返回值类型:在C++11中,lambda 表达式的返回值是通过返回值后置语法来定义的。
- 函数体:函数的实现,这部分不能省略,但函数体可以为空。
lambda 表达式的大致原理:每当你定义⼀个 lambda 表达式后,编译器会自动⽣成⼀个匿名类(这
个类重载了()运算符),我们称为闭包类型(closure type)。
那么在运行时,这个 lambda 表达式就会返回⼀个匿名的闭包实例,其实⼀个右值。所以,lambda 表
达式的结果,本质上就是闭包实例。
捕获列表
[]
- 不捕捉任何变量[&]
- 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获
),修改是全局的[=]
- 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获
),由于拷贝的副本在匿名函数体内部是只读的,变量的值为 const 属性,不允许修改[=, &foo]
- 按值捕获外部作用域中所有变量, 并按照引用捕获外部变量 foo[bar]
- 按值捕获 bar 变量, 同时不捕获其他变量[&bar]
- 按引用捕获 bar 变量, 同时不捕获其他变量[this]
- 捕获当前类中的this指针(其实是复制指针),可以让 lambda 表达式拥有和当前类成员函数同样的访问权限,如果已经使用了 & 或者 =,则默认添加此选项。[*this]
- 通过传值方式捕获当前对象
为什么通过值拷贝的方式捕获的外部变量是只读的:
- lambda表达式的类型在C++11中会被看做是一个带 operator() 的类,即仿函数。
- 按照C++标准,lambda 表达式的 operator() 默认是 const 的,一个 const 成员函数是无法修改成员变量值的。mutable 选项的作用就在于取消 operator() 的 const 属性。
lambda 表达式⼀个很重要的应用是其可以用于函数的参数,通过这种方式可以实现回调函数。其实,最常用的是在STL算法中,比如你要统计⼀个数组中满足特定条件的元素数量,通过 lambda 表达式给出条件,传递给 count_if 函数:
int val = 3;
vector<int> v {1, 8, 5, 3, 6, 10};
int count = std::count_if(v.beigin(), v.end(), [val](int x) { return x > val; });
// v中⼤于3的元素数ᰁ