lambda表达式是C++11引入的新特性,用于声明一个函数,因为不需要对这个函数指定函数名,故lambda表达式声明的是匿名函数。lambda来源于函数式编程的概念,也是现代编程语言的一个特点。
lambda表达式具有如下优点:
(1)声明式编程风格:匿名定义函数,不需要额外命名函数,可以更直接地写程序。
(2)在需要的时间和地点实现功能闭包,使编写程序更加灵活。因为其不需要额外命名函数,避免了代码膨胀和功能分散,使得程序更加简洁。
lambda表达式的语法如下:
[caputrue](params)opt -> ret { body; };
(1)caputrue是捕获列表。指明了lambda表达式能访问的外部变量(lambda表达式函数体之外的变量),以及如何访问这些变量。具体情况如下:
[] 不捕获任何外部变量
[&] 按引用捕获,即捕获外部作用域中所有变量,并作为引用在函数体中使用。
[=] 按值捕获,即捕获外部作用域中所有变量,并作为值副本在函数体中使用。
[=,&a] 按值捕获外部作用域中所有变量,并按引用捕获a变量。
[b] 按值捕获b变量,同时不捕获其他变量。
[this] 应用于类中。捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样的访问权限,从而可以在lambda中使用当前类的成员函数和成员变量。如果已经在捕获列表中使用了&或者=,那么就默认添加了该选项。
(2)params是参数列表。表示传给lambda表达式的参数序列,选填。
(3)opt是函数选项。可以填mutable,exception,attribute(选填一个或者多个)。
mutable:说明lambda表达式体内的代码可以修改被捕获的变量,如果被捕获的变量是一个对象则可以调用该对象的non-const函数。
exception:说明lambda表达式是否抛出异常。
attribute:用来声明函数属性。
(4)ret是返回类型,选填。
(5)body是函数体。
下面以在类中声明lambda表达式为例:
class A{
public:
int a = 0;
void fun(int x, int y){
auto a1 = []{ return a; }; //error,没有捕获外部变量
auto a2 = [=] { return a + x + y; }; //ok
auto a3 = [&] { return a + x + y; }; //ok
auto a4 = [this] { return a; }; //ok
auto a5 = [this]{return a + x + y; }; /*error,因为x,y不是类成员变量,而且没有捕获x,y*/
auto a6 = [this, x, y]{ return a + x + y; }; //ok
auto a7 = [this]{ return a ++; }; //ok
}
注意事项:
1、lambda表达式的延迟调用。lambda表达式按值捕获外部变量时,在捕获的瞬间,外部变量的值就被复制了,之后该外部变量值的改变不会对之前捕获到的值有影响。如果希望lambda表达式在调用时即时访问外部变量,应该使用引用方式捕获。
int a = 0;
auto func1 = [=] { return a; };
a ++;
std::cout << func1() << std::endl; //输出0
int b = 0;
auto func2 = [&b]{ return b; };
b ++;
std::cout << func2() << std::endl; //输出1
2、按值捕获外部变量时,在lambda表达式中修改它们的副本并不会影响外部的值,但我们仍然无法修改这些副本。如果想修改这些副本,需要显示声明lambda表达式为mutable。lambda表达式定义的是仿函数闭包。lambda表达式捕获到的任何外部变量,最终均会变为闭包类型的成员变量。按照C++11标准,lambda表达式的operator()默认是const的,一个const成员函数是无法修改成员变量的值的。而mutable就取消了operator()的const。
注:被mutable修饰的lambda表达式就算没有参数也要显示写明参数列表。
int a = 0;
auto func1 = [=] { return a++; }; //error
auto func2 = [=] () mutable { return a++; }; //ok