在C++中,一共有四种可调用对象:函数、函数指针、重载了函数调用运算符的类以及Lambda表达式。
对Lambda的总结如下:
一个Lambda表达式就是一个可调用的代码单元,也相当于一个未命名的内联函数,一个lambda表达式的标准形式:
可以忽略参数列表和返回类型,但是必须包含捕获列表和函数体,调用方式与普通函数相同。
auto fun=[ ]{return 42};
cout<<fun()<<endl;//输出42
参数列表为空,忽略返回类型时,根据函数体推断返回类型,如果函数体中只有一个return语句,则根据返回表达式类型推断,否则,返回类型为void
(1)向lambda传递参数
[ ](const string& a,const string& b) { return a.size()>b.size();}
捕获列表也就是[ ]为空时,说明不使用所在函数的任何局部变量,当需要使用所在函数的局部变量时,则需要放在捕获列表内。
例如:void find(){
int sz=10;
auto wc=find_if(words.begin(),words.end(),[sz] (const string &a ) { return a.size()>=sz;} );
}
lambda中使用了所在函数中定义的局部(非static)变量sz,则必须放进捕获列表中,否则出错。
lambda可以直接使用定义在当前函数之外的名字。
(2)捕获和返回
lambda 有两种捕获方式: 显式捕获以及隐式捕获,其中 显式捕获又包括值捕获和引用捕获。lambda的数据成员在lambda对象创建时被初始化。
(2.1) 显式捕获
值捕获
例如:void f1(){
size_t v1=1;
auto f=[v1] {return v1;}
v1=0;
auto j=f();//此时j等于1 ,f保存了创建对象时v1的拷贝
}
引用捕获
例如:void f1(){
size_t v1=1;
auto f=[&v1] {return v1;}
v1=0;
auto j=f();//此时j等于0 ,f保存了v1的引用
}
使用引用捕获时,必须保证被引用的对象在lambda执行的时候是存在的,因为引用必须初始化。可以从函数中返回一个lambda,但是不能包含引用捕获,因为这相当于函数返回了一个局部变量的引用。
(2.2)隐式捕获
在捕获列表中写一个& 表示引用捕获,写一个=表示值捕获
例如:
void find(){
int sz=10;
auto wc=find_if(words.begin(),words.end(),[=] (const string &a ) { return a.size()>=sz;} );
}
sz采用隐式捕获,值捕获的方式。
隐式捕获与显式捕获可以混合使用,例如:
当混合使用时,捕获列表中的第一个元素必须是&或者=,指定了默认的捕获方式,不采用默认的话,就必须显式捕获方式命名变量。
(3)可变lambda:
对于值捕获方式,默认lambda不会改变其值,如果想改变的话,在参数列表后加上关键字mutable。
例如:void f1(){
size_t v1=1;
auto f=[v1] ()mutable {return ++v1;}
v1=0;
auto j=f();//此时j等于2
}
而对于引用捕获的方式,变量能否修改则依赖于引用指向的是const类型还是非const类型。
(4)指定lambda的返回类型
lambda函数体内除了return语句还有其他语句,默认返回void,如果想要指定返回值类型,在参数列表后加 ->+类型。
例如:
transform(vi.begin(),vi.end(),[ ](int i){return i<0? -i:i;});
该lambda返回一个int值,但是此时因为函数体只有return语句,所以无须指定返回类型,类型可以推断出来。
transform(vi.begin(),vi.end(),[ ](int i){if(i<0) return -i;else return i;});
此时由于函数体内还有其他语句,所以不能推断返回类型,编译错误,必须指定返回类型:
transform(vi.begin(),vi.end(),[ ](int i) -> int {if(i<0)return -i;else return i;});
捕获列表为空,接受单一int参数,返回一个int值,必须指定返回类型。