
目录一、lambda表达式1.1 概念1.2 捕捉列表1.3 lambda的应用1.4 lambda原理二、包装器2.1 function应用场景2.2 bind常用场景一调整参数个数绑定个别参数常用场景二成员函数调用一、lambda表达式1.1 概念C11引入的Lambda表达式是一种就地定义的匿名函数对象。它允许你在需要函数的地方直接写一段逻辑而不必提前定义一个命名函数或手写一个完整的仿函数类。在学习lambda表达式之前我们的使用的可调用对象只有函数指针和仿函数对象函数指针的类型定义起来比较麻烦仿函数要定义一个类重载()相对会比较麻烦。使用lambda去定义可调用对象既简单又方便。与普通函数不同lambda表达式可以在函数内部实现。语法格式[捕捉列表](参数)-returntype{函数体}举个简单的例子autof[](inta,intb)-int{returnab;};coutf(2,3)endl;说明开头的 [ ]必须要写因为编译器根据[ ]来判别其为lambda表达式()即为参数列表如果不需要参数()可以省略- return type返回值类型也可以省略编译器会自动推导{ }即函数体与普通函数一样。1.2 捕捉列表顾名思义就是将函数体中会用到的变量对象等进行抓取使得其在函数体内部可用。voidtest(){inta2,b3;autoAdd[a,b](){returnab;};coutAdd()endl;// 调用}那么捕捉有什么讲究呢具体有四种捕捉方式值捕捉我们在上面直接将变量名或对象在捕捉列表捕捉就是值捕捉。引用捕捉引用捕捉就是在变量前加。voidtest(){inta2,b3;autoAdd[a,b](){returnab;};coutAdd()endl;// 调用}显示捕捉我们在捕捉列表直接写明要捕捉的变量或对象就是显示捕捉。显示捕捉时既可以值捕捉也可以引用捕捉同样也可以在一个捕捉列表中同时出现值捕捉和引用捕捉。隐式捕捉隐式捕捉有隐式值捕捉即直接在捕捉列表中写一个 隐式引用捕捉即在捕捉列表直接写一个 。需要注意在用到隐式捕捉时 或 必须写在捕捉列表最前面两者不能同时出现。如果此时想混合捕捉且前面已经是 那么后面必须全部是引用捕捉同理如果前面已经是 那么后面必须全部是值捕捉。voidtest(){inta2,b3,c4;autoAdd1[](){returnabc;};// 隐式值捕捉autoAdd2[](){returnabc;};// 隐式引用捕捉// 混合捕捉autoAdd3[,b](){returnabc;};autoAdd4[,c](){returnabc;};// 错误示范:隐式值捕捉后面又显示值捕捉重复// auto Add2 [, b, c]() {return a b c; };}细节补充默认情况下值捕捉是被const修饰的也就是说值捕捉的过来的对象不能修改mutable加在参数列表的后面可以取消其常量性也就说使用该修饰符后值捕捉的对象就可以修改了但是修改还是形参对象不会影响实参。使用该修饰符后参数列表不可省略(即使参数为空)。引用捕捉的对象可以修改。intmain(){inta1,b1,c1,d1;autof[a,b,c,d](){// a; // 报错// b; // 报错c;d;};f();// 调用coutc c, d dendl;return0;}intmain(){inta1,b1,c1,d1;autof[a,b,c,d]()mutable{a;b;returnab;};coutf()endl;// 调用// 并不改变实参couta a, b bendl;return0;}对于全局变量和静态局部变量不需要捕捉lambda表达式中可以直接使用。这也意味着lambda表达式如果定义在全局位置捕捉列表必须为空。1.3 lambda的应用对于需要自定义排序的类对象我们不再需要重载operator()而是直接用lambda表达式。#includevector#includealgorithm#includestringstructProduct{std::string _name;double_price;int_stock;Product(std::string name,doubleprice,intstock):_name(name),_price(price),_stock(stock){}};structCompare{booloperator()(constProductp1,constProductp2){returnp1._pricep2._price;}};intmain(){std::vectorProductproducts{{iPhone,5999.0,50},{MacBook,12999.0,20},{AirPods,1299.0,200}};// 重载operator()std::sort(products.begin(),products.end(),Compare());// lambda表达式按价格降序std::sort(products.begin(),products.end(),[](constProducta,constProductb){returna._priceb._price;});return0;}1.4 lambda原理其实就是编译器在底层将我们写的lambda表达式转换成了一个仿函数实际上并没有所谓的lambda表达式。autof[x,y](inta)-int{returnxay;};// 上面的 Lambda编译器会翻译成类似下面的类class__lambda_unique_id{// 编译器生成的唯一类名intx;// 值捕获的成员拷贝inty;// 引用捕获的成员引用public:__lambda_unique_id(int_x,int_y):x(_x),y(_y){}intoperator()(inta)const{// 函数调用运算符returnxay;}};// 实际调用时// auto f __lambda_unique_id(x, y);// f(5); // 等价于 f.operator()(5);二、包装器2.1 function什么是function就是将具有相同类型返回值参数列表的可调用对象进行包装让可调用对象对外呈现一种类型其头文件为functional。可调用对象有4个函数指针仿函数lambda和bindbind是什么我们下面介绍四种可调用对象四种完全不同的类型intplain_func(int,int){return0;}// 函数指针类型int(*)(int,int)structFunctor{intoperator()(int,int){}};// 仿函数类型Functorautolambda[](int,int){return0;};// lambda类型编译器生成的匿名类autoboundstd::bind(plain_func,_1,_2);// bind类型std::_Bind...std::function可以包装存储这些对象这些对象成为function的目标。若std::function不含目标则称它为空。调用空std::function的目标就会导致抛出std::bad_function_call异常。function的用法std::function返回类型(参数类型列表)f;所以对于上面的四个可调用对象就可以进行统一包装std::functionint(int,int)f1plain_func;// ✅std::functionint(int,int)f2Functor();// ✅std::functionint(int,int)f3lambda;// ✅std::functionint(int,int)f4bound;// ✅应用场景用function对象调用成员函数痛点由于成员函数第一个参数默认为this指针当我们将成员函数直接赋值给function对象就会报错。解决lambda捕捉this。classA{public:intAdd(inta,intb){returnab;}};intmain(){A a;// 想要调用成员函数// func_t func a.Add(2, 3); // 报错// 解决std::functionint(int,int)func[a](intx,inty){returna.Add(x,y);};coutfunc(2,3)endl;return0;}2.2 bindstd::bind也是一个可调用对象的包装器相当于函数适配器其头文件也是functional。有什么特点呢它解决的核心问题固定部分参数留下部分参数延迟传入。bind用法autonewCallbind(Call,arg_list);// 如果想要调用类A中的成员函数CallautonewCallbind(A::Call,arg_list);// 注意格式必须是类名::成员函数名其中newCall本身是一个可调用对象arg_list是一个逗号分隔的参数列表对应给定的Call的参数。当我们调用newCall时newCall会调用Call并传给它arg_list中的参数。为什么说bind支持固定参数呢arg_list中的参数可能包含形如_n的名字其中n是一个整数这些参数是占位符表示newCall的参数它们占据了传递给newCall的参数的位置。数值n表示生成的可调用对象中参数的位置_1为Call的第一个参数_2为第二个参数以此类推。_1/_2/_3…这些占位符放到placeholders的一个命名空间中。intSub(intx,inty){return(x-y)*10;}voidtest01(){autofbind(Sub,_1,_2);coutf(2,3)endl;}voidtest02(){autofbind(Sub,_2,_1);// 调整参数顺序coutf(2,3)endl;}常用场景一调整参数个数绑定个别参数此时对于一些固定不变的参数就不需要再传了voidtest03(){autof1bind(Sub,5,_1);coutf1(3)endl;autof2bind(Sub,_1,5);coutf2(3)endl;}常用场景二成员函数调用structA{intSub(inta,intb){return(a-b)*10;}};voidTest(){A a;autof1bind(A::Sub,a,_1,_2);// function包装std::functionint(int,int)f2bind(A::Sub,a,_1,_2);coutf1(2,3)endl;coutf2(5,2)endl;}std::bind绑定成员函数时把对象指针/引用作为第一个参数传入就填上了 this 的位置生成的可调用对象不再需要外部提供 this编译器视角是返回类型(类名, 参数列表)*同样可以解决我们调用成员函数时this指针的问题。这些特性在未来写代码的过程中其实非常常见也很实用希望能够帮助到大家上面其实就有我所遇到的场景如果再有什么实用的场景我也会及时地补充。