lambda表达式
是C++11最重要也是最常用的特性之一,这是现代编程语言的一个特点,lambda表达式
有如下的一些优点:
lambda表达式
定义了一个匿名函数,并且可以捕获一定范围内的变量。lambda表达式
的语法形式简单归纳如下
[capture](params) opt -> ret {body;};
其中capture是捕获列表
,params是参数列表
,opt是函数选项
,ret是返回值类型
,body是函数体
。
捕获列表[]: 捕获一定范围内的变量
参数列表(): 和普通函数的参数列表一样,如果没有参数参数列表可以省略这个小括号不写。
函数选项, 不需要可以省略
mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用throw();
返回值类型:在C++11中,lambda表达式的返回值是通过返回值后置语法来定义的。
函数体:函数的实现,这部分不能省略,但函数体可以为空。
注意:参数列表(): 和普通函数的参数列表一样,如果没有参数参数列表可以省略这个小括号不写。
auto f = [](){return 1;} // 没有参数, 参数列表为空
auto f = []{return 1;} // 没有参数, 参数列表省略不写
lambda表达式的捕获列表可以捕获一定范围内的变量,具体使用方式如下:
让lambda表达式拥有和当前类成员函数同样的访问权限
如果已经使用了 & 或者 =, 默认添加此选项
#include
#include
using namespace std;
class Test
{
public:
void output(int x, int y)
{
this->m_number = 10000;
auto x1 = [] {return m_number; }; // 错误,没有捕获外部变量,不能使用函数外部定义的类成员 m_number
auto x2 = [=] {return m_number + x + y; }; // 正确,以值拷贝的方式捕获所有外部变量
auto x3 = [&] {return m_number + x + y; }; // 正确,以引用的方式捕获所有外部变量
auto x4 = [this] {return m_number; }; // 正确,捕获this指针,可访问对象内部成员
auto x5 = [this] {return m_number + x + y; }; // 错误,捕获this指针,可访问类内部成员,没有捕获到变量x,y,因此不能访问
auto x6 = [this, x, y] {return m_number + x + y; }; // 正确,捕获this指针,x,y
auto x7 = [this] {return m_number++; }; // 正确,捕获this指针,并且可以修改对象内部变量的值
}
int m_number = 100;
int m_number1 = 1000;
};
解释
x1:错误,没有捕获外部变量,不能使用类成员 m_number x2:正确,以值拷贝的方式捕获所有外部变量
x3:正确,以引用的方式捕获所有外部变量
x4:正确,捕获this指针,可访问对象内部成员
x5:错误,捕获this指针,可访问类内部成员,没有捕获到变量x,y,因此不能访问。
x6:正确,捕获this指针,x,y
x7:正确,捕获this指针,并且可以修改对象内部变量的值
在匿名函数内部,需要通过lambda表达式的捕获列表控制如何捕获外部变量,以及访问哪些变量。默认状态下lambda表达式无法修改通过值拷贝方式捕获的外部变量,如果希望修改这些外部变量,需要通过引用的方式进行捕获。
举例
#include
#include
using namespace std;
void func(int x, int y)
{
int a=100;
int b;
auto f=[a, &x]()
{
int c = a;
x=x+5;
a++;
cout << "a=" << a;
return x;
}();
cout << "a=" << a;
}
int main()
{
func(1,5);
return 0;
}
很多时候,lambda表达式的返回值是非常明显的,因此在C++11中允许省略lambda表达式的返回值。
// 完整的lambda表达式定义
auto f = [](int a) -> int
{
return a+10;//返回值是整数类型
};
// 忽略返回值的lambda表达式定义
auto f = [](int a)
{
return a+10;
};
一般情况下,不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值的类型,但需要注意的是labmda表达式不能通过列表初始化自动推导出返回值类型。
// ok,可以自动推导出返回值类型
auto f = [](int i)
{
return i;
}
// error,不能推导出返回值类型
auto f1 = []()
{
return {1, 2}; // 基于列表初始化推导返回值,错误
}
使用lambda表达式
捕获列表捕获外部变量,如果希望去修改按值捕获的外部变量,那么应该如何处理呢?这就需要使用mutable选
项,被mutable修改是lambda表达式就算没有参数也要写明参数列表()
,相当于去掉按值捕获的外部变量的只读(const)属性。
#include
#include
using namespace std;
void func(int x, int y)
{
int a=100;
int b;
auto f=[a, &x]() mutable
{
int c = a;
x=x+5;
a++;
cout << "a=" << a;
return x;
}();
cout << "a=" << a;
}
int main()
{
func(1,5);
return 0;
}
最后再剖析一下为什么通过值拷贝的方式捕获的外部变量是只读的:
lambda表达式的类型在C++11中会被看做是一个带operator()
的类,即仿函数。
按照C++标准,lambda表达式的operator()默认是const的,一个const成员函数是无法修改成员变量值的。
mutable选项的作用就在于取消operator()的const属性。
参考博客