lambda表达式
C++98的栗子
在C++98中,如果要对数据进行排序,当待排数据类型是内置类型时:可以有多种选择,比如,冒泡,归并或者快排;如果待排数据是自定义类型,则需要创建相应的仿函数
举个栗子:将每种球类按照价格的升序进行排序
struct Goods { string _name; double _price; Goods(const char* str, double price) :_name(str) , _price(price) {} }; //按照降序进行排 struct Comparepriceless { bool operator()(const Goods& g1, const Goods& g2) { return g1._price < g2._price; } }; int main() { vector<Goods> v = {{"篮球",50 },{"排球",30},{"羽毛球",20}}; sort(v.begin(), v.end(), Comparepriceless()); return 0; }
运行结果
随着语言的发展,人们觉得上面的方式太麻烦,每次为了实现一个仿函数都是要重新写一个类,如果比较的逻辑不同,还要去实现不同的类,带来了很大的不方便;因此,C++11中出现了lambda表达式来解决这个问题
lambda表达式
使用lambda表达式,修改上面的代码
struct Goods { string _name; double _price; Goods(const char* str, double price) :_name(str) , _price(price) {} }; int main() { vector<Goods> v = { {"篮球",50 },{"排球",30},{"羽毛球",20} }; sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._price < g2._price; }); return 0; }
运行结果与上面一致
lambda表达式语法
语法:[capture-list](parameters)multable->return-type{statement}
[capture-list]:捕捉列表,编译器根据捕捉列表中所捕捉的变量(上文)提供给lambda使用;必须写
(parameters):参数列表,与普通参数列表一致;可省略
multable:默认情况下,lambda函数总是一个const函数,multable可以取消其常量性;使用该修饰时,参数列表不可以省略;可省略
->return-type:返回值类型,可自动推导返回值类型从而声明函数的返回值类型;可省略
{statement}:函数体,在该函数体内,除了可以使用其参数外,还可以使用所捕捉的变量
举个栗子:
int main() { //最简单的lambda表达式,无任何意义 [] {}; auto compare = [](int x, int y) {return x > y; }; cout << compare(1, 0) << endl; return 0; }
lambda表达式实际上是一个对象,类型无法获得,只能通过auto去推演
捕捉列表说明
[var]:表示值传递方式捕捉变量var
[=]:表示值传递方式捕捉所有父作用域中的变量;所谓父作用域便是lambda函数的语句块
[&var]:表示引用传递方式捕捉变量[var]
[&]:表示引用传递方式捕捉所有父作用域中的变量
函数对象与lambda表达式
观察下列代码
class Rate { public: Rate(double rate) :_rate(rate) {} //仿函数 double operator()(double money, int year) { return money * _rate * year; } private: double _rate; }; int main() { double rate = 0.4; Rate r1(rate); r1(10000, 5); auto r2 = [=](double money, int year) {return money * rate * year; }; r2(10000, 5); return 0; }
仿函数与lambda表达式完全一样;仿函数将rate作为其成员变量,在定义对象时给出初始值即可;lambda表达式通过捕获列表可以直接将该变量捕获到
仿函数反汇编
lambda表达式反汇编
包装器
function包装器
function本质是一个类模板,也是一个包装器
观察下列代码
template<class F,class T> T useF(F f, T x) { static int i = 0; cout << "i:" << ++i << endl; cout << "i:" << &i << endl; return f(x); } double f(double d) { return d / 2; } struct Functor { double operator()(double d) { return d / 2; } }; int main() { //函数名f cout << useF(f, 1.1) << endl; //仿函数Functor cout << useF(Functor(), 1.1) << endl; //lambda表达式 cout << useF([](double d){ return d / 4; }, 1.1) << endl; return 0; }
运行结果
首先变量i是静态,本应该生成一份,但结果却是生成了三份,而且代码中调用的方式并不统一;接下来使用function统一调用方式
类模板原型如下
Ret:被调用函数的返回类型 Args...:被调用函数的形参 template<class Ret,class... Args> class function<Ret(Args...)>
举个栗子:
int f(int a, int b) { return a + b; } struct Functor { int operator()(int a, int b) { return a + b; } }; int main() { //函数名/函数指针 function<int(int, int)> f1(f); cout << f1(1, 2) << endl; //仿函数 function<int(int, int)>f2=Functor(); cout << f2(1, 2) << endl; //lambda表达式 function<int(int, int)>f3 = [](const int a, const int b) {return a + b; }; cout << f3(1, 2) << endl; return 0; }
包装器的可以将函数指针/仿函数/lambda表达式进行类型统一,使用一种方式进行调用
bind
上面的function是类模板,这里的bind是一个函数模板,接受一个可调用对象,生成一个新的可调用对象来适配原对象的参数列表
原型如下
template<class Ret,class... Args> bind(Fn&& fn,Args&&... args)
这里的参数包是由命名空间placeholders构成
举个栗子:
int Plus(int a, int b) { return a + b; } int Sub(int a, int b) { return a - b; } int main() { //绑定函数Plus,参数分别调用func1的第一个,第二个参数 function<int(int, int)>func1 = bind(Plus, placeholders::_1, placeholders::_2); cout << func1(1, 2) << endl; //绑定函数Sub,参数分别调用func2的第一个,第二个参数 function<int(int, int)>func2 = bind(Sub, placeholders::_1, placeholders::_2); cout << func2(1, 2) << endl; return 0; }