lambda是C++11非常重要的特性之一
优点
- 可以就地匿名定义目标函数或函数对象,不需要写一个函数
- lambda是一个匿名的内联函数,在被调用的地方会被直接展开,避免了函数调用的开销。
语法
[ 捕获列表 ]( 参数列表 ) -> 返回值类型 { 函数体 } ;
- 要是没有参数列表可以省略
- 捕获列表是对于函数捕获外围变量的权限
捕获列表
- [ ] 不捕获任何变量
- [ & ] 引用捕获外部作用域中的所有变量
- [ = ] 值捕获外部作用域中的所有变量 值拷贝来的副本也只是可读的
- [ =,&a ] 值捕获外部作用域中的所有变量,引用捕获外部变量a
- [ a ] 值捕获a变量 不捕获其他变量
- [ this ] 捕获当前类的this指针,让lambda表达式拥有和当前类成函数同样的访问权限
- [ *this ] C++17之后可以按值捕获实例
- [ k=7 ] C++14可以定义新的对象并且初始化 无需再外围环境中
参数列表
C++14还支持参数列表使用auto类型,如[](auto a,auto b){return a+b};只要是支持+运算符的都可以适用,类型模板。
返回值
-> 返回值类型一般可以省略,编译器根据return语句自动推导返回值类型。
但是注意lambda不能通过列表初始化自动推导返回值类型。
lambda与函数指针
- 函数指针定义在别的地方,代码阅读不方便
- 效率上,比较于使用内联函数的指针,使用函数指针可能导致编译器不对其进行inline优化。比较于非内联函数,在调用次数多的情况下,lambda避免函数开销,提升效率。
代码
class A
{
private:
int pri;
public:
void play() { };
void fun(int x, int y)
{
auto f1 = [this, x, y]()
{
play();
pri = x;
return pri + x + y;
};//成功:捕获类this 获取类成员成员函数相同权限 可读 可修改
}
};
int main()
{
int a = 1, b = 2;
auto f1 = []() {return a; }; //错误:未捕获a 无法访问a
auto f2 = [&a]() { a++; }; //成功:引用捕获a 可以直接对a操作
auto f3 = [a]() {a++; }; //错误:值捕获a 副本也是可读不可改
auto f4 = [=](int c) {c = a; return a; };//成功:值捕获全局 返回int类型
auto f5 = [&] {a++; int c = a; b = c; }; //成功:引用捕获全局 参数列表可以省略
//值捕获a 引用捕获b的几种方式
auto f6 = [a, &b] {};
auto f6 = [=, &b] {};
auto f6 = [&, a] {};
//返回值一般可忽略
//错误: 但是不能是列表初始化 无法给自动lambda提供返回值
auto f7 = [](){return { 1,2,3 }; };
//正确:显示声明函数的返回值
auto f7 = []()->vector<int>{return { 1,2,3 }; };
}
vector<int>vec{ 7,9,4,1,6,2 };
//实例使用:利用lambda改变sort的排序顺序
void text1() {
auto f = [](int a, int b) {
return a > b;
};
sort(vec.begin(), vec.end(), f);
//也可以直接用lambda表达式——返回闭包对象:匿名函数
sort(vec.begin(), vec.end(), [](int a, int b) {
return a > b;
}
);
}
//内联函数指针和lambda
inline void func(int i)//内联函数
{
if (i >= 5)
vec.push_back(i);
}
void text2() {
//传统迭代器for循环
for (auto itr = vec.begin(); itr != vec.end(); ++itr)
{
if (*itr >= 5)
vec.push_back(*itr);
}
//使用内联函数指针和for_each
for_each(vec.begin(), vec.end(),func);
//使用lambda和for_each
for_each(vec.begin(), vec.end(), [=](int i) {
if (i >= 5)
vec.push_back(i);
}
);
}