lambda表达式
一、lambda表达式
1.1 lambda表达式的语法
[capture-list] (parameters) mutable -> return-type { statement};
[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
注意:
在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。
#include<iostream>
using namespace std;
int main()
{
int x = 10, y = 20;
auto Add1 = [](int a, int b)->int{return a + b;};//没有省略任何一个部分(mutable除外)
auto Add2 = [](int a, int b){return a + b;};//在返回值类型明确下省略返回值类型
auto Add3 = [x,y]{return x + y;};//使用捕获列表捕获上下文变量(按值捕获const类型),不使用函数参数列表,返回值类型明确。
cout << Add1(x, y) << endl;
cout << Add2(x, y) << endl;
cout << Add3() << endl;
return 0;
}
上述代码执行结果:
注意:在省略掉(parameters)的时候,->return-type也得省略,否则编译会报错
二、lambda表达式各部分说明
2.1 捕捉列表和参数列表说明
捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。
- [var]:表示值传递方式捕捉变量var
- [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
- [&var]:表示引用传递捕捉变量var
- [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
- [this]:表示值传递方式捕捉当前的this指针
#include<iostream>
using namespace std;
int main()
{
int x = 10, y = 20;
auto Swap1 = [](int x, int y){int temp = x; x = y; y = temp;};
auto Swap2 = [](int &x, int &y){int temp = x; x = y; y = temp;};
int a = 30, b = 40;
auto Swap3 = [a, b]()mutable{int temp = a; a = b; b = temp;};
auto Swap4 = [&a, &b]{int temp = a; a = b; b = temp;};
Swap1(x, y);
cout << "Swap1:: " << x << " " << y << endl;
Swap2(x, y);
cout << "Swap2:: " << x << " " << y << endl;
Swap3();
cout << "Swap3:: " << a << " " << b << endl;
Swap4();
cout << "Swap4:: " << a << " " << b << endl;
return 0;
}
上面代码得运行结果:
我们发现Swap1和Swap3并没有发生交换,具体分析一下原因:
int x = 10, y = 20;
auto Swap1 = [](int x, int y){int temp = x; x = y; y = temp;};
auto Swap2 = [](int &x, int &y){int temp = x; x = y; y = temp;};
Swap1和Swap2都使用函数参数列表,但是不同的是,Swap1是传值,Swap2是传引用。传值传递给形参的是一种拷贝,形参的改变并不会影响实参。传引用传递给形参的是实参的别名,因为是别名,所以对形参的改变也会影响实参。
int a = 30, b = 40;
auto Swap3 = [a, b]()mutable{int temp = a; a = b; b = temp;};
auto Swap4 = [&a, &b]{int temp = a; a = b; b = temp;};
Swap3和Swap4都是使用捕捉列表,但不同的是Swap3是按值捕捉,而Swap4是按引用捕捉。按值捕捉也是捕捉上下文变量的拷贝,只不过是const属性的,所以这里就可以用到mutable移除const属性,但是即使是移除了,他还是拷贝,它的改变并不会影响原来变量的值。按引用捕捉就是捕捉上下文变量的别名,它的改变会改变上下文变量的值。
2.2 参数列表、mutable和->return-type之间配合使用的关系
(parameters) 和 -> return-type:
在省略参数列表的时候,->return-type也得省略,不然会编译报错。
(parameters) 和 mutable:
在省略参数列表的时候,mutable也得省略,不然会编译报错。
2.3 仿函数与lambda表达式
struct Rate
{
Rate(double rate): _rate(rate)
{}
double operator()(double money, int year)
{ return money * _rate * year;}
private:
double _rate;
};
int main()
{
// 函数对象
double rate = 0.49;
Rate r1(rate);
r1(10000, 2);
// lamber
auto r2 = [=](double money, int year)->double{return money *rate *year;};
r2(10000, 2);
return 0;
}
从使用方式上来看,函数对象与lambda表达式完全一样。
函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可
以直接将该变量捕获到。
实际在底层编译器对于lambda表达式的处理方式,完全就是按照仿函数的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。
三、捕捉列表的灵活使用
3.1 [=]与[&]
-
[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
-
[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
#include<iostream>
using namespace std;
int main()
{
int a = 1, b = 2, c = 3, d = 4, ret = 0;
auto Add = [=, &ret]{ret = a + b + c + d;};
Add();
cout << ret << endl;
return 0;
}
如果上下文需要获取许多变量,并不需要一个一个的捕捉,只需要学会使用[=]和[&]即可, 例如上面的[=, &ret],他就是用值捕捉所有上下文变量,用引用捕捉ret,值捕捉和引用捕捉是可以混着一起用的,只需要用逗号隔开就好,例如[=, &d, &ret],还是比较灵活的。