
lambda表达式是C++11的一个非常重要的一个语法,可以帮助我们简介代码,如果说右值引用是减轻了编译器的负担,那么lambda表达式就是减轻了程序员写代码的负担。
一、C++98的一些问题
在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法C++提供了仿函数less和greater 可以去控制sort是升序还是降序。
cpp
#include <algorithm>
#include <functional>
int main()
{
int array[] = {4,1,8,5,3,7,0,9,2,6};
// 默认按照小于比较,排出来结果是升序
std::sort(array, array+sizeof(array)/sizeof(array[0]));
// 如果需要降序,需要改变元素的比较规则
std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());
return 0;
}
但是如果待排序的元素是自定义类型,那么我们需要自己去针对性地写一个符合我们需求的仿函数传过去
cpp
struct Goods
{
string _name; // 名字
double _price; // 价格
int _evaluate; // 评价
Goods(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{}
};
struct ComparePriceLess
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price < gr._price;
}
};
struct ComparePriceGreater
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price > gr._price;
}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
3 }, { "菠萝", 1.5, 4 } };
sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());
}
随着C++语法的发展,**人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便(1、想要知道传的是什么仿函数,不方便找。2、命名如果不规范,可读性差)。**因此,在C++11语法中出现了Lambda表达式用来解决这样的问题。
二、lambda表达式的语法
lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement}
(1)[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来
判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda
函数使用。
(2)(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以
连同()一起省略
(3)mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量
性。使用该修饰符时,参数列表不可省略(即使参数为空)
(4)->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回
值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推
导。
(5){statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获
到的变量。
注意:在lambda函数定义中,参数列表和返回值类型都是可选部分 ,而捕捉列表和函数体可以为
空。mutable一般用的很少。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。
三 深入分析捕捉列表
3.1 捕获列表基本用法
如果我们要实现一个lambda表达式的交换函数,那么我们可以很容易的通过传引用去交换两个对应的参数
cpp
int main{
int x = 0, y = 1;
int m = 0, n = 1;
auto swap1 = [](int& rx, int& ry)
{
int tmp = rx;
rx = ry;
ry = tmp;
};
swap1(x, y);
cout << x << " " << y << endl;
}
我们尝试用捕捉列表来解决这个问题
cpp
int main()
{
int x = 0, y = 1;
int m = 0, n = 1;
auto swap1 = [](int& rx, int& ry)
{
int tmp = rx;
rx = ry;
ry = tmp;
};
swap1(x, y);
cout << x << " " << y << endl;
// 传值捕捉
auto swap2 = [x, y]() mutable
{
int tmp = x;
x = y;
y = tmp;
};
swap2();
cout << x << " " << y << endl;
//引用捕捉
auto swap3 = [&x, &y]()
{
int tmp = x;
x = y;
y = tmp;
};
swap3();
cout << x << " " << y << endl;
}

3.2 捕获列表的进阶用法
var\]:表示值传递方式捕捉变量var(**默认是const** )
\[=\]:表示值传递方式捕获所有父作用域中的变量(**包括this**)

\[\&var\]:表示引用传递捕捉变量var
\[\&\]:表示引用传递捕捉所有父作用域中的变量(包括this)
\[this\]:表示值传递方式捕捉当前的this指针
注意事项:
a. 父作用域指包含lambda函数的语句块
b. **语法上捕捉列表可由多个捕捉项组成,并以逗号分割。**
比如:\[=, \&a, \&b\]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量
\[\&,a, this\]:值传递方式捕捉变量a和this,引用方式捕捉其他变量
c. **捕捉列表不允许变量重复传递,否则就会导致编译错误**。
比如:\[=, a\]:=已经以值传递方式捕捉了所有变量,捕捉a重复
d. **在块作用域以外的lambda函数捕捉列表必须为空。(写在函数体外)**
e. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量**(现在已经改成只要变量在lambda函数定义的词法作用域内可见,比如爷爷作用域,就可以被捕获),捕捉任何非此作用域或者非局部变量都会导致编译报错。(全局变量 或者是其他作用域的变量)**
f. **lambda表达式之间不能相互赋值**,即使看起来类型相同(要了解底层原理!)
```cpp
void (*PF)();
int main()
{
auto f1 = []{cout << "hello world" << endl; };
auto f2 = []{cout << "hello world" << endl; };
// 此处先不解释原因,等lambda表达式底层实现原理看完后,大家就清楚了
//f1 = f2; // 编译失败--->提示找不到operator=()
// 允许使用一个lambda表达式拷贝构造一个新的副本
auto f3(f2);
f3();
// 可以将lambda表达式赋值给相同类型的函数指针
PF = f2;
PF();
return 0;
}
```
## 四、lambda表达式替换仿函数
```cpp
int main()
{
vector