前言
lambda表达式是C++11新特性之一,下面一起来看一下lambda表达式。
1、lambda表达式
先来看一下lambda表达式的语法形式:
[ capture ] ( params ) opt -> ret { body; };
其中carpture是捕获列表,params是参数,opt是选项,ret则是返回值的类型,body则是函数的具体实现。
1.1、carpture捕获列表描述了lambda表达式可以访问上下文中的哪些变量。
[] :表示不捕获任何变量
[=]:表示按值捕获变量
[&]:表示按引用捕获变量
[this]:值传递捕获当前的this
但是捕获列表不允许变量的重复传递:例如[=,x]
上面这种捕获是不允许的,=表示按值的方式捕获所有的变量,x相当于被重复捕获了。
1.2、params表示lambda的参数,用在{}中。
1.3、opt表示lambda的选项,例如mutable,后面会介绍一下mutable的用法。
1.4、ret表示lambda的返回值
也可以显示指明返回值,lambda会自动推断返回值,但是值得注意的是只有当lambda的表达式仅有一条return语句时,自动推断才是有效的。像下面这种的表达式就需要加上返回类型。
[](double x )->double{int y = x ;return x - y;};
2、lambda表达式简单示例
2.1、采用lambda来替换fun7函数。与fun7函数对应的lambda表达式如下:
[](int x) {return x%7==0;}
因此 ,我们可以直接使用上面的表达式直接替换fun7,代码如下:
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
//为判断元素是否能被13整除,可以使用下列的函数:
//bool fun7(int x)
//{
// return x%13==0;
//}
int main()
{
vector<int> vec(10);
//generate()的用法,该函数接受一个区间,由前两个参数指定,并将区间中的每个元素设置为第三个参数返回的值
generate(vec.begin(),vec.end(),rand);
for(int i = 0;i<10;i++)
cout << vec[i] << " ";
cout << endl;
//count_if()算法和generate()函数差不都,同样前两个参数也是指定了一个区间,第三个参数则是一个bool 类型的函数对象
//int count = count_if(vec.begin(),vec.end(),fun7);
int count = count_if(vec.begin(),vec.end(),[](int x){return x % 7 == 0;} //使用lambda表达式替换fun7函数
);
cout << count << endl;
return 0;
}
2.2、lambda表达式是匿名函数,实际上也可以给lambda表达式指定一个名称,如下表示:
auto f = [](int x ){return x % 3 ==0;};
此后再需要使用该lambda表达式,就可以使用f()来代替。举一个例子:
#include <iostream>
using namespace std;
int main()
{
int a = 5,b = 6;
auto f = [=]{return a+b;};//[=]按值捕获了a和b
cout << f() << endl;
return 0;
}
运行程序输出结果为11,由上栗看出可以给lambda指定一个名称,再用该名称来取代调用该表达式。
2.3、按值捕获和按引用捕获
按值捕获和按引用捕获的用法通过下面这个例子来看一下。
#include <iostream>
using namespace std;
int main()
{
int a = 5;
auto f1 = [=]{return a+1;};//按值捕获a
auto f2 = [&]{return a+1;};//按引用捕获a
cout << f1() << endl;
cout << f2()<< endl;
a++;
cout << f1() << endl;
cout << f2() << endl;
return 0;
}
程序运行结果如下:
先看第一次f1和f2的输出,都是6,这没有什么问题。再执行a++,输出f1和f2,f2的结果是7 也没有问题,为什么f1还是输出6呢?答案就是按值捕获可以理解为一旦lambda按值捕获某个变量相当于在表达式内部已经生成了一个被捕获变量的副本,而lambda表达式使用的就是这个副本,原本的变量再怎么变化都不会影响到副本的值,所以f1 lambda表达式中的值一直都是捕获时a 的值 也就是5,后续a++的操作和f1表达式没有关系。简单可以理解为f1表达式赋值了一个和a同名的const变量。从这我们也可以得出一个结论:
如果希望lambda函数在调用时访问的外部变量是最新的,我们就需要使用按引用捕获。
2.4、按值捕获[=],修改lambda中变量的值,关键字mutable使用示例
下面可以看一下这个例子:
#include <iostream>
using namespace std;
int main()
{
int a = 5;
auto f = [=]{return a*=5;};//按值捕获a
cout << f() << endl;
return 0;
}
运行程序,有如下编译报错:
提示a是一个只读的,不允许修改,这就验证上面例子中说明的按值捕获实际上是lambda拷贝了一个与被捕获变量同名的const 副本并进行操作。
如果实在需要改变lambda中的值,这时就需要使用上文提到过的选项mutable。
默认情况下,lambda函数是一个const函数,而mutable也可以取消常量性。例如有如下代码:
#include <iostream>
using namespace std;
int main()
{
int a = 5;
auto f = [=]()mutable{return a*=5;};//取消常量性
cout << f() << endl;
return 0;
}
这里需要注意,被 mutable 修饰的 lambda 表达式就算没有参数也要写明参数列表。
运行程序如下:
可以看到原先lambda函数中的a是只读的,加上mutable就可以修改lambda函数中a的值了。