1、lambda表达式是c++11 最重要也最常用的一个特性之一。lambda来源于函数式编程的概念,也是现代编程语言的一个特点。
lambda有以下优点:
声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序,好的可读性和可维护性。
简洁:不需要额外在写一个函数或函数对象,避免了代码膨胀和功能分散,让开发者更加击中精力在手边的问题,同时也获得了更高的生产率。
在需要的时间和地点实现功能闭包,是程序更灵活。
2、语法如下:
[capture] ( params ) opt -> ret { body; };
其中:
capture是捕获列表;params是参数表;opt是函数选项;ret是返回值类型;body是函数体。
auto f = [ ] (int a) -> int { return a + 1; };
std::cout << f(1) <<std::endl; //输出2
很多时候lambda的表达式的返回值是很明显的,c++11中允许省略lambda的表达式:
auto f = [ ] (int a) { return a + 1; };
需要注意的是,初始化列表不能用于返回值的自动推导:
auto x = [ ] ( ) { return { 1, 2 }; }; //error:无法推导出返回值类型
另外,lambda 表达式在没有参数列表时,参数列表是可以省略的。因此,像下面的下发都是正确的(注意和上面区分):
auto f1 = [ ]( ) { return 1; };
auto f2 = [ ] { return 1; };
lambda 表达式可以通过捕获列表捕获一定范围内的变量:
- [] 不捕获任何变量。
- [&]捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
- [=]捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
- [=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获foo 变量。
- [bar]按值捕获bar 变量,同时不捕获其他变量。
- [this]捕获当前类中的this 指针,让lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 &或者=,就默认添加此选项
class A{
public:
int i=0;
void fun(int x,int y){
auto x1=[]{ return i;}; //error,没有捕获外部变量
auto x2=[=]{return i+x+y;};
auto x3=[&]{return i+x+y;};
auto x4=[this]{return i;};
auto x5=[this]{return i+x+y;}; //error,没有捕获x, y
auto x6=[this, x, y]{return i+x+y;};
auto x7=[this]{return i++;};
}
};
int main(){
int a=0,b=1;
auto f1=[]{return a;}; //error,没有捕获a变量
auto f2=[&]{return a++;};
auto f3=[=]{return a;};
auto f4=[=]{return a++;}; //error,a是以复制方式捕获的,无法修改
auto f5=[a]{return a++;}; //error,a只读,不能修改
auto f6=[a]{return a;};
}
在上面的代码中,auto f4=[=]{return a++;};这是以复制方式捕获,无法修改,相当于const,如果我想修改它呢?
显然是有办法的,显示指明lambda 表达式为mutable:
int a=0;
auto f = [=] ( ) mutable { return a++; }; //这是编译器允许的,可以在本地编译器试一试。
需要注意一点的是,被mutable 修饰的lambda 表达式就算没有参数也要写明参数列表。
3、lambda表达式类型
最后,介绍一下lambda 表达式的类型。
lambda 在c++11中被称为 “闭包类型”。它是一个特殊的,匿名的非 nunion 的类类型。
因此,我可以们认为它是一个带有 operator() 的类,即仿函数。因此,我们可以调用std::function 和 std::bind 来存储和操作 lambda 表达式:
std::function<int(int)> f1=[ ](int a){ return a;};
std::function<int(void)> f2=std::bind( [ ] (int a) { return a; }, 123 );