Lambda表达式
C++11的一大特性就是引入了Lambda表达式,并在后续的C++14进行了加强。
利用Lambda表达式,可以方便定义和创建匿名函数。
一个Lambda表达式表示一个可调用单元,我们可以将其理解为一个未命名的内联函数。
Lambda语法定义
[capture list] (paramters list) mutable exception-> return type{function body}
其中——
- capture list,捕获外部变量列表;
- paramters list,形参列表;
- mutable,用来说明是否可以修改捕获的外部变量;
- exception,抛出异常;
- return type,返回类型;
- function body,函数体。
除此之外,还可以缺省部分声明构成不完整的Lambda表达式,常见的有以下几种——
序号 | 格式 |
---|---|
1 | [capture list] (params list) -> return type {function body} |
2 | [capture list] (params list) {function body} |
3 | [capture list] {function body} |
其中——
- 格式1省略了mutable关键字和exception,声明的是一个const类型的表达式,这种表达式不能修改捕获列表中的值;
- 格式2 省略了返回类型return type,但是编译器可以根据functionbody中的return语句推断出Lambda表达式的返回类型,如果没有return语句,默认为void类型;
- 格式3中省略了参数列表,即无参的Lambda表达式。
举个使用Lambda表达式的例子——
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
vector<int> v{12,52,67,87,34,95,16,48};
//使用格式3的Lambda表达式定义sort排序规则
sort(v.begin(),v.end(),
[](int&a,int&b){return a<b;});
for(auto&elem:v){
cout<<elem<<" ";
}
return 0;
}
Lambda捕获外部参数
Lambda表达式可以使用其可见范围内的外部变量,但必须明确声明。主要通过中括号[]来捕获外部变量,比如——
#include<iostream>
using namespace std;
int main(){
int a = 100;
auto f = [a]{cout<<a<<endl;};
f();// f相当于一个无参的函数调用
return 0;
}
这种方式有别于传统的函数参数,增加了语言使用的灵活性。
Lambda表达式的外部变量捕获有4种方式:值捕获、引用捕获、隐式捕获和混合捕获。
值捕获
值捕获与函数传参数相似,都是按值传递,外部变量的改变不会影响表达式已捕获变量的值——
#include<iostream>
using namespace std;
int main(){
int a = 100;
auto f = [a]{cout<<a<<endl;};
a = 200;// 此时改变a的值
f();// 依旧输出100,而不是200
return 0;
}
需要注意的是,捕获的外部变量是const的,无法修改。
如果需要修改,需添加mutable关键字,括号也不能省略,比如说——
#include<iostream>
using namespace std;
int main(){
int a = 100;
auto f = [a]()mutable{cout<<++a<<endl;};
a = 200;//
f();
return 0;
}
引用捕获
顾名思义,就是捕获一个外部变量的引用,只需要在捕获的外部变量前面加上一个&即可——
#include<iostream>
using namespace std;
int main(){
int a = 100;
auto f = [&a]{cout<<a<<endl;};
a = 200;// 此时改变a的值
f();// 输出200
a = 300;
f();//输出300
return 0;
}
隐式捕获
有时候,捕获的变量比较多的情况,一一书写变量名,可能就会很麻烦。
针对这种情况,Lambda提供了一种隐式捕获的方法,让编译器自行推断需要捕获哪些变量。
我们只需要定义捕获的方式是值捕获还是引用捕获,这就是隐式捕获。
具体使用方法,举例如下——
#include<iostream>
using namespace std;
int main(){
int a = 100;
int b = 200;
int c = 300;
int d = 400;
auto f = [=]{
cout<<"a = "<<a<<endl
<<"b = "<<b<<endl
<<"c = "<<c<<endl
<<"d = "<<d <<endl;};// 值捕获
f();
return 0;
}
只需要把=
改成&
,就变成引用捕获了,在此不再举例。
混合方式
Lambda还支持以上几种方式的混合使用,具体语法,总结如下——
序号 | 语法 | 作用 |
---|---|---|
1 | [] | 不捕获任何外部变量 |
2 | [args …] | 默认是值捕获的方式,如果需要引用捕获,需要单独在变量名前加& |
3 | [this] | 以值捕获的形式捕获this指针 |
4 | [=] | 以值捕获的形式捕获所有外部变量 |
5 | [&] | 以引用捕获的方式捕获所有的外部变量 |
6 | [=,&x] | 变量x以引用捕获,其余值捕获 |
7 | [&,x] | 变量x值捕获,其余引用捕获 |
Lambda参数列表
Lambda表达式的参数列表和函数的参数列表类似,但是又有所不同,具体表现为——
- 不支持默认参数列表
- 不支持可变参数列表
- 所有参数必须有参数名
相同之处在于,Lambda表达式的参数列表同样可以作为局部变量看待,能够覆盖同名的全局变量。