1.为什么需要有lamdba?
:很多泛型算法都有一个允许我们传递可调用对象(或可调用表达式)的版本(我们将这个可调用的对象或表达式暂称为谓词),例如:版本一ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator last) ,版本二ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator last, BinaryPredicate binary_pred)。可调用对象(或可调用表达式)包括重载了调用运算符的类,函数,指向函数的指针,以及lambda。但是泛型算法对谓词的参数个数有限制(只有一元谓词或二元谓词),lambda就是为了解决这个限制。
2.介绍lamdba
[capture list] (parameter list) ->return type { function body; }
capture list::捕获列表,由于lambda可能定义在函数内部,捕获列表就是一个lambda所在函数中定义的局部变量的列表,lamdba通过将局部变量包含在其捕获类表中来指出将会使用这些变量,捕获列表只能用于局部非static变量;lambda可以直接使用定义在当前函数之外的名字和局部static变量;
parameter list:参数列表,lamdba不能有默认实参
return type:返回类型,必须使用置尾返回
function body:函数体
可以忽略参数列表和返回类型,但是必须永远包含捕获列表和函数体。如果忽略返回类型,lamdba根据函数体中的代码推断返回类型:如果函数体只有个return一条语句,则返回类型从返回的表达式的类型推断而来,否则,返回类型为空。
3.为什么lambda具有以上特性?
其实在定义一个lamdba时,编译器会生成一个与lamdba对应的新的(未命名的)类类型。当向一个函数传递一个lambda时,同时定义了一个新类型和该类型的一个对象:传递的参数就是编译器生成的类类型的未命名对象。类似的,当使用auto定义一个用lamdba初始化的变量时,定义了一个从lamdba生成的类型的对象。默认情况下,通过lamdba生成的类都包含一个对应该lamdba所捕获的变量的数据成员,类似任何普通类的数据成员,lamdba的数据成员在lamdba对象创建时被初始化。
4.捕获列表
值捕获:采用值捕获的前提是对象可以拷贝。被捕获的变量的值是在lamdba创建时拷贝,而不是调用时拷贝,因此随后对被捕获变量的修改不会影响到lamdba内对应的值。
引用捕获:一个以引用方式捕获的变量与其他任何类型的引用行为类似。当我们在lamdba函数体中使用此变量时,实际上使用的是引用所绑定的对象。引用捕获的主要限制是:必须确保被引用的对象在lamdba执行时是存在的。lamdba不活的都是局部变量,这些变量在函数结束后就不存在了。如果lamdba可能在函数结束后执行,捕获的引用指向的局部变量就消失了。我么可以从一个函数返回lamdba,与函数不能返回一个局部变量的引用类似,此lamdba也不能包含引用捕获。
隐式捕获/显式捕获:让编译器根据lamdba函数体中的代码来推断我们要使用哪些变量,此时我们应该在捕获列表中写一个&(捕获引用方式)或=(采用值捕获方式)。
5.可变lamdba
值捕获变量:默认情况下,对于一个值被拷贝的变量,lamdba不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表与函数体之间加上关键字mutable。
引用捕获方式:一个引用捕获的变量是否可以修改依赖于引用指向的变量是一个const类型还是非const类型。