【C++】C++11:lambda、包装器

发布时间:2026/7/19 3:22:09
【C++】C++11:lambda、包装器 C11lambda、包装器1、lambda表达式1.1 lambda表达式语法lambda 表达式本质是一个匿名函数对象跟普通函数不同的是他可以定义在函数内部。我们⼀般是用auto或者模板参数定义的对象去接收lambda 对象。#includeiostreamusingnamespacestd;intmain(){// [] 捕获列表() 参数- 返回值可省略{} 函数体autofunc[](){cout这是lambda匿名函数endl;};// 调用lambda// 输出这是lambda匿名函数func();return0;}lambda表达式的格式[capture-list] (parameters)- return type { function boby }[capture-list]捕捉列表捕捉外面的变量供lambda函数使用捕捉列表为空也不能省略(parameters)lambda的形参列表用来传参与普通函数的参数列表功能类似如果不需要参数传递则可以连同()一起省略-return type返回值类型指定返回值类型一般返回值类型明确情况下可以省略由编译器对返回类型进行推导{function boby}函数体和普通函数大括号完全一样写逻辑、循环、判断、return、cout 输出全都放这里。egintmain(){autoadd1[](intx,inty)-int{returnxy;};coutadd1(1,2)endl;// 输出3// 1、捕捉为空也不能省略// 2、参数为空可以连同()一起省略// 3、返回值可以省略可以通过返回对象自动推导// 4、函数体不能省略autofunc1[]{couthelloendl;return0;};func1();// 输出hellointa0,b1;autoswap1[](intx,inty){inttmpx;xy;ytmp;};swap1(a,b);couta:bendl;// 输出1:0return0;}1.2 lambda和sort排序使用sort排序structGoods{string _name;// 名字double_price;// 价格int_evaluate;// 评价// ...Goods(constchar*str,doubleprice,intevaluate):_name(str),_price(price),_evaluate(evaluate){}};// 按价格升序structCompare1{booloperator()(constGoodsgl,constGoodsgr){returngl._pricegr._price;}};// 按价格降序structCompare2{booloperator()(constGoodsgl,constGoodsgr){returngl._pricegr._price;}};// 按评价分数降序structCompare3{booloperator()(constGoodsgl,constGoodsgr){returngl._evaluategr._evaluate;}};intmain(){vectorGoodsv{{苹果,2.1,5},{香蕉,3,4},{橙子,2.2,3},{菠萝,1.5,4}};sort(v.begin(),v.end(),Compare1());sort(v.begin(),v.end(),Compare2());sort(v.begin(),v.end(),Compare3());}使用sort后可读性较差逻辑复杂使用lambda表达式修改#includevector#includealgorithmstructGoods{string _name;// 名字double_price;// 价格int_evaluate;// 评价// ...Goods(constchar*str,doubleprice,intevaluate):_name(str),_price(price),_evaluate(evaluate){}};intmain(){// 价格降序autopriceLess[](constGoodsgl,constGoodsgr){returngl._pricegr._price;};sort(v.begin(),v.end(),priceLess);sort(v.begin(),v.end(),[](constGoodsgl,constGoodsgr){returngl._pricegr._price;});sort(v.begin(),v.end(),[](constGoodsgl,constGoodsgr){returngl._evaluategr._evaluate;});sort(v.begin(),v.end(),[](constGoodsgl,constGoodsgr){returngl._evaluategr._evaluate;});}用lambda表达式后不用单独写一个struct仿函数不需要额外定义类1.3 捕捉列表lambda表达式中默认只能用lambda 函数体和参数中的变量如果想用外层作用域中的变量就需要进行捕捉第一种在捕捉列表中显示的传值捕捉[var]和传引用捕捉[var]捕捉的多个变量用逗号分割intx0;intmain(){// 只能用当前lambda局部域和捕捉的对象和全局对象inta0,b1,c2,d3;autofunc1[a,b]()mutable{// 值捕捉的变量不能修改引用捕捉的变量可以修改//a;b;x;intretab;returnret;};coutfunc1()endl;// 输出3}第二种在捕捉列表中隐式捕捉[]表示隐式值捕捉[]表示引用方式传递捕捉所有父作用域中的变量这样我们lambda表达式中⽤了那些变量编译器就会自动捕捉那些变量intmain(){inta0,b1,c2,d3;// 隐式值捕捉// 用了哪些变量就捕捉哪些变量autofunc2[]{intretabc;returnret;};coutfunc2()endl;// 输出3// 隐式引用捕捉// 用了哪些变量就捕捉哪些变量autofunc3[]{a;c;d;};func3();// 输出1 1 3 4couta b c dendl;}第三种在捕捉列表中混合使用隐式捕捉和显示捕捉[, x]表示其他变量隐式值捕捉x引用捕捉[, x, y]表示其他变量引用捕捉x和y值捕捉intmain(){// 只能用当前lambda局部域和捕捉的对象和全局对象inta0,b1,c2,d3;// 混合捕捉1autofunc4[,a,b]//其他都是引用捕捉a和b是值捕捉不能放到中间{//a;//b;c;d;returnabcd;};func4();// 输出0 1 3 4couta b c dendl;// 混合捕捉2autofunc5[,a,b]//其他都是值捕捉a和b是引用捕捉{a;b;/*c; d;*/returnabcd;};func5();// 输出1 2 3 4couta b c dendl;return0;}注意值传递方式捕捉的变量默认情况下是被const修饰的也就是说传值捕捉的变量不能修改如果想修改可以在参数列表的后面加mutable取消其常量性例如autofunc7[]()mutable{a;// a可修改b;c;d;returnabcd;};coutfunc7()endl;couta b c dendl;语法上捕捉列表可以有多个项组成例如[, a, b]其他都是引用捕捉a和b是值捕捉只能放在列表的开头[a, , b]是错误的捕捉列表不允许变量重复传递否则会导致编译出错例如[, a]出现了两个值捕捉导致重复2、functionstd::function是一个类模板是C提供的函数包装器类模板不管是普通函数、函数指针、仿函数、lambda、bind绑定出来的可调用对象std::function相当于一个统一的盒子能把它们全部装进去统一形成一种类型templateclassTclassfunction;// undefinedtemplateclassRet,class...ArgsclassfunctionRet(Args...);//eg:接收两个int参数返回int的可调用对象盒子functionint(int,int)函数指针、仿函数、lambda等可调用对象的类型各不相同std::function的优势就是统一类型对他们都可以进行包装这样在很多地方就方便声明可调用对象的类型#includefunctional// 计算两数相加intf(inta,intb){returnab;}// 仿函数结构体structFunctor{public:intoperator()(inta,intb){returnab;}};// 包含多种函数classPlus{public:// 构造函数Plus(intn10):_n(n){}// 静态成员函数staticintplusi(inta,intb){returnab;}// const普通成员函数doubleplusd(doublea,doubleb)const{return(ab)*_n;}private:int_n;};intmain(){//函数指针// void(*pf)(int) nullptr;// 包装各种可调用对象functionint(int,int)f1f;// 全局函数functionint(int,int)f2Functor();// 仿函数functionint(int,int)f3[](inta,intb){returnab;};// lamdbacoutf1(1,1)endl;// 2coutf2(1,1)endl;// 2coutf3(1,1)endl;// 2// 把function放进vector容器中vectorfunctionint(int,int)vf{f,Functor(),[](inta,intb){returnab;}};for(autof:vf){coutf(1,1)endl;// 输出2}// 包装静态成员函数// 成员函数要指定类域并且前面加才能获取地址functionint(int,int)f4Plus::plusi;coutf4(1,1)endl;// 普通成员函数隐藏第一个参数即this参数所以要加上Plus*functiondouble(Plus*,double,double)f5Plus::plusd;Plus ps;coutf5(ps,1.1,1.1)endl;// 输出22// 第二种包装plusd接收const对象引用// const Plus 可以接收普通对象、临时对象不用传指针//functiondouble(Plus, double, double) f6 Plus::plusd;functiondouble(constPlus,double,double)f6Plus::plusd;coutf6(Plus(),1.1,1.1)endl;// Plus()是临时对象匹配const引用输出22coutf6(ps,1.1,1.1)endl;// 传入局部对象ps输出22// 右值引用版本Plus仅能接收临时对象、std::move转换后的右值对象functiondouble(Plus,double,double)f7Plus::plusd;coutf7(Plus(),1.1,1.1)endl;// Plus()临时右值匹配Plus输出22coutf7(move(ps),1.1,1.1)endl;// move把ps转为右值匹配Plus输出22return0;}总结三种传对象的方式区别Plus*传对象地址const Plus万能普通对象、临时对象都能接Plus只接收临时右值或者 move 转换后的对象3、bindbind是一个函数模板也是一个可调用对象的包装器接收任意可调用对象返回一个新的可调用对象它可以用来调整参数个数和参数顺序// 版本1自动推导返回值templateclassFn,class...Args/* unspecified */bind(Fnfn,Args...args);// 版本2手动指定返回值templateclassRet,classFn,class...Args/* unspecified */bind(Fnfn,Args...args);bind的一般形式auto newCallable bind(callable,arg_list);newCallable可调用对象callable代表原始可调用对象就是你想要改装的函数arg_list是逗号分隔的参数分两种类型1、固定常量数字 / 变量 / 对象2、占位符 _1 _2 _3_1为newCallable的第⼀个参数_2为第⼆个参数以此类推#includefunctionalusingplaceholders::_1;usingplaceholders::_2;usingplaceholders::_3;intSub(inta,intb){return(a-b)*10;}intSubX(inta,intb,intc){return(a-b-c)*10;}intmain(){coutSub(10,5)endl;// bind 本质返回的一个仿函数对象// 调整参数顺序不常用// _1代表第一个实参// _2代表第二个实参// ...// _2填给Sub的第一个形参a_1 填给 Sub 的第 2 个形参 bautonewSub1bind(Sub,_2,_1);coutnewSub1(10,5)endl;// 调整参数个数// _1填给Sub的a常量10固定填给Sub的bautonewSub2bind(Sub,_1,10);coutnewSub2(20)endl;// 常量5固定填给Sub的a_1填给Sub的bautonewSub3bind(Sub,5,_1);coutnewSub3(20)endl;// 常量5填给SubX的a_1填给SubX的b_2填给SubX的cautonewSub4bind(SubX,5,_1,_2);coutnewSub4(10,20)endl;// _1填给SubX的a常量5填给SubX的b_2填给SubX的cautonewSub5bind(SubX,_1,5,_2);coutnewSub5(10,20)endl;// _1填给SubX的a_2填给SubX的b常量5填给SubX的cautonewSub6bind(SubX,_1,_2,5);coutnewSub6(10,20)endl;写_n调用新函数时传入的第 n 个实参会填充到这个原函数形参位置