【C++】C++11新特性—可变参数模版、lambda、包装器
目录一、可变参数模版二、emplace_back三、lambda表达式四、包装器4.1 function4.2 bind一、可变参数模版C语言中有可变函数参数比如我们熟悉的printf和scanfC中有时候模版也需要可变参数C11的新特性可变参数模板可以接受可变参数的函数模板和类模板而C11之前的类模版和函数模版中只能含固定数量的模版参数。下面是一个可变参数的函数模版templateclass...ArgsvoidShowList(Args...args){}Args是一个类型参数包args是一个函数形参参数包声明一个参数包Args...args这个参数包中可以包含0到任意个模板参数这里的可变指的是参数类型是任意的参数个数也是任意的这意味着我们可以像下面这样使用templateclass...ArgsvoidShowList(Args...args){}intmain(){ShowList();ShowList(1);ShowList(1,2.2);ShowList(1,2.2,3333);//...return0;}以前我们实现的模版能接受多种类型但是参数的个数却是确定的。可变参数模版不仅能接受多种类型而且参数个数任意因此可以看作是模版的模版。这里实际是编译器帮我们生成了四个函数ShowList()、ShowList(int x)、ShowList(int x, double y)、ShowList(int x, double y, std::string str)。我们也可以用sizeof()看一下各个函数的参数个数这里sizeof()的用法怎么看怎么奇怪谁知道呢人家确实是这么设计的。如果我们想打印模版参数包怎么操作呢这里有一个前提它一定要在编译时推导参数。我们无法直接获取参数包args中的每个参数只能通过展开参数包的方式这是使用可变模版参数的一个主要特点也是最大的难点即如何展开可变模版参数。有一个办法是用类似递归的方法处理voidPrint(){coutendl;}templateclassT,class...ArgsvoidPrint(Tx,Args...args){coutx ;Print(args...);}templateclass...ArgsvoidShowList(Args...args){Print(args...);}intmain(){ShowList(1,2.2,3333);return0;}注意上面args的用法除了sizeof...都在args的后面。从上面的示例可以看到可变参数模版确实给我们提供了很大的方便的但是麻烦事终归是不可避免的那只能交给编译器来帮我们干这些繁琐的活了感恩编译器。❤️二、emplace_backemplace_back支持模板的可变参数还有万能引用。上面插入有名对象的使用场景中emplace_back和push_back是一样的。不一样的地方是插入匿名对象emplace_back支持用构建对象的参数自己去创建对象省去了拷贝构造/移动构造这一步骤。如果是push_back插入匿名对象它还是会先构造一个对象再拿这个对象去拷贝构造目标对象。emplace_back支持了模版的可变参数及万能引用所以这个过程去掉了中间值也就是第一步构造的对象而是直接去构造目标对象。| 实现自己的emplace_back只需要把参数包不断往下传递最后根据参数包构造或拷贝构造目标对象。传到最后如果参数包是已经存在的对象就调用对应的拷贝构造如果是还未构建对象的参数就构造。测试发现这里应该调移动构造啊怎么调了构造构造呢其中的原因在上篇文章中有介绍过右值引用的本身是左值。所以上面的参数包在传递过程中都需要用完美转发来保持原生类型属性。以emplace_back为例说实话这个使用的格式有点让人难受。实现emplace_back://...templateclass...Argslist_node(Args...args):_data(forwardArgs(args)...),_next(nullptr),_prev(nullptr){}//...templateclass...Argsvoidemplace_back(Args...args){insert(end(),forwardArgs(args)...);}//...templateclass...Argsiteratorinsert(constiteratorpos,Args...args){Node*pcurpos._node;Node*prevpcur-_prev;Node*newnodenewNode(forwardArgs(args)...);prev-_nextnewnode;newnode-_prevprev;newnode-_nextpcur;pcur-_prevnewnode;_size;returnnewnode;}//...总结就是如果插入有名对象push_back和emplace_back是一样的但是插入匿名对象构造对象的参数push_back还会先构造再拷贝构造而emplace_back是不断往下传递参数包最后根据参数包中的数据来匹配是直接构造对象还是调用相应的拷贝构造。所以整体来说还是emplace_back略胜一筹。三、lambda表达式如果我们要对自定义类型排序就不能使用算法库中的sort函数解决只能自己用仿函数自定义排序规则structGoods{Goods(constchar*str,doubleprice,intevaluate):_name(str),_price(price),_evaluate(evaluate){}string _name;//名字double_price;//价格int_evaluate;//评价};structComparePriceLess{booloperator()(constGoodsgl,constGoodsgr){returngl._pricegr._price;}};structComparePriceGreater{booloperator()(constGoodsgl,constGoodsgr){returngl._pricegr._price;}};intmain(){vectorGoodsv{{苹果,2.1,5},{香蕉,3,4},{橙子,2.2,3},{菠萝,1.5,4}};sort(v.begin(),v.end(),ComparePriceLess());sort(v.begin(),v.end(),ComparePriceGreater());return0;}但这样有点繁琐每次为了完成某种比较都要写一个类如果自定义类型中的成员变量很多而且都要通过比较来排序那就要实现很多个类。为此C11提出了lambda表达式来解决这个问题。lambda表达式书写格式[capture-list] (parameters) mutable - return-type { statement }[capture-list]捕捉列表编译器根据[]来判断下面的代码是否为lambda函数其作用是捕捉上下文中的变量供lambda函数使用(parameters)参数列表和普通函数参数列表一样如果不需要传参则()可省略mutable默认lambda函数是一个const函数mutable可以取消其常性使用时参数列表总是不能省略- return-type返回值类型没有返回值、返回值类型明确时可省略{ statement }函数体除了可以使用其参数外还可使用所有捕获到的变量lambda表达式的类型没有名称我们通常无法直接引用它但可以使用auto关键字来存储lambda表达式的实例。有了lambda表达式上面通过仿函数来实现排序可以写成intmain(){vectorGoodsv{{苹果,2.1,5},{香蕉,3,4},{橙子,2.2,3},{菠萝,1.5,4}};sort(v.begin(),v.end(),[](constGoodsg1,constGoodsg2){returng1._priceg2._price;});sort(v.begin(),v.end(),[](constGoodsg1,constGoodsg2){returng1._priceg2._price;});return0;}上述代码就是使用C11中的lambda表达式来解决代码明显更简洁可以看出lambda表达式实际是一个匿名函数对象。lambda表达式捕捉列表说明[var]表示值传递方式捕捉变量var[]表示值传递方式捕获所有父作用域中的变量(包括this)[var]表示引用传递捕捉变量var[]表示引用传递捕捉所有父作用域中的变量(包括this)其中父作用域指包含lambda函数的语句块语法上捕捉列表可由多个捕捉项组成并以逗号分割[, a, b]以引用传递的方式捕捉变量a和b值传递方式捕捉其他所有变量[a, this]值传递方式捕捉变量a和this引用方式捕捉其他变量捕捉列表不允许变量重复传递否则就会导致编译错误比如[, a]已经以值传递方式捕捉了所有变量捕捉a重复在块作用域以外的lambda函数捕捉列表必须为空在块作用域中的lambda函数仅能捕捉父作用域中局部变量捕捉任何非此作用域或者非局部变量都 会导致编译报错lambda表达式之间不能相互赋值即使看起来类型相同允许使用一个lambda表达式拷贝构造一个新的副本仿函数是在类中重载了operator()使之能像函数一样可以调用。classRate{public:Rate(doublerate):_rate(rate){}doubleoperator()(doublemoney,intyear){returnmoney*_rate*year;}private:double_rate;};intmain(){// 函数对象doublerate0.49;Rater1(rate);r1(10000,2);// lambdaautor2[](doublemonty,intyear)-double{returnmonty*rate*year;};r2(10000,2);return0;}从上面的代码中可以看到仿函数的使用和lambda函数的使用是一样的那lambda函数的底层是怎样的呢函数对象将rate作为其成员变量在定义对象时给出初始值即可lambda表达式通过捕获列表可以直接将该变量捕获到捕获的本质是构造函数初始化参数。lambda表达式的底层也是调用重载的operator().四、包装器4.1 function使用function需要包头文件functional。模板参数说明Ret: 被调用函数的返回类型Args…被调用函数的形参function包装器也叫作适配器。C中的function本质是一个类模板也是一个包装器。function可包装任何类型的可调用对象函数指针、仿函数、lambda。intfunc(inta,intb){returnab;}structFunc{intoperator()(inta,intb){returnab;}};intmain(){//包装可调用对象functionint(int,int)f1func;functionint(int,int)f2Func();functionint(int,int)f3[](inta,intb){returnab;};coutf1(1,2)endl;coutf2(1,2)endl;coutf3(1,2)endl;return0;}从汇编层可以看到function包装器仅仅是在外面进行了包装其底层都还是调用的operator()。function的特点可以用来做类型统一比如在下面的这个例题中逆波兰表达式求值classSolution{public:intevalRPN(vectorstringtokens){stackint_st;for(autoe:tokens){if(e||e-||e*||e/){inta_st.top();_st.pop();intb_st.top();_st.pop();switch(e[0]){case:_st.push(ba);break;case-:_st.push(b-a);break;case*:_st.push(b*a);break;case/:_st.push(b/a);break;default:break;}}else{_st.push(stoi(e));}}return_st.top();}};可以看出这道题的关键就是遇到运算符就执行对应的运算操作我们可以用mapfunctionlambda进行简单的包装从而得出这个题的新玩法classSolution{public:intevalRPN(vectorstringtokens){stackintst;mapstring,functionint(int,int)func_map{{,[](inta,intb){returnab;}},{-,[](inta,intb){returna-b;}},{*,[](inta,intb){returna*b;}},{/,[](inta,intb){returna/b;}}};for(autostr:tokens){if(func_map.find(str)!func_map.end()){intast.top();st.pop();intbst.top();st.pop();st.push(func_map[str](b,a));}else{st.push(stoi(str));}}returnst.top();}};另外使用function包装可调用对象时类型一定要匹配类型不匹配就会报错。在这个点上特别需要注意的是类的成员函数。比如classFunc{public:staticintfunc1(inta,intb){returnab;}//非静态成员函数doublefunc2(doublea,doubleb){returnab;}};intmain(){functionint(int,int)f1Func::func1;//静态成员函数functiondouble(double,double)f2Func::func2;//非静态成员函数return0;}成员函数受类域限制需要指定类域非静态成员函数取函数指针时要符号静态成员函数可以不加但建议还是都加上非静态成员函数在类内部取函数指针也需要指定类域静态成员函数就不需要上面的包装有错误你能发现吗事实上上面的包装有一处是类型不匹配的就是对非静态成员函数的包装。用function包装非静态成员函数时不要忘了它还有一个隐含的this指针而静态成员函数是没有this指针的。包装非静态成员函数下面两种方式都可以//方式一functiondouble(Func*,double,double)f2Func::func2;Func f;coutf2(f,1.1,2.2)endl;//方式二functiondouble(Func,double,double)f3Func::func2;coutf3(Func(),1.1,2.2)endl;为什么一种是对象的指针另一种直接是对象呢因为这里并不是直接把对象指针或者对象传给函数func2事实上this指针也不支持这样传function的底层还是调用的operator()而不管是指针还是对象都可以调用成员函数。4.2 bindbind函数也是定义在头文件functional中是一个函数模版它就像一个函数包装器(适配器)接受一个可调用对象生成一个新的可调用对象来“适应”原对象的参数列表。一般而言我们用它可以把一个原本接收N个参数的函数fn通过绑定一些参数返回一个接收M个M可以大于N但这么做没什么意义参数的新函数。同时使用bind函数还可以调整参数顺序。调用bind的一般形式auto newCallable bind(callable, arg_list);newCallable本身是一个可调用对象arg_list是一个逗号分隔的参数列表对应给定的callable的参数。当我们调用newCallable时newCallable会调用callable并传给它arg_list中的参数。arg_list中的参数可能包含形如_n的名字其中n是一个整数这些参数是“占位符”表示newCallable的参数它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置_1为newCallable的第一个参数_2为第二个参数以此类推。1、调整参数顺序不常用2、调整参数个数常用再看上面function包装非静态成员函数每次调用都要传对象或对象指针有点麻烦可以用bind来绑死这个固定参数。bind本质返回一个仿函数对象因此也可以用function包装。//方式二functiondouble(Func,double,double)f3Func::func2;coutf3(Func(),1.1,2.2)endl;functiondouble(double,double)f4bind(Func::func2,Func(),_1,_2);coutf4(1.1,2.2)endl;bind绑定成员函数#includefunctional#includeiostreamclassMyClass{public:voidmemberFunction(intx){std::coutMember function called with x x and this thisstd::endl;}voidsetupCallback(){// 使用std::bind绑定memberFunction并传递this指针autoboundFuncstd::bind(MyClass::memberFunction,this,std::placeholders::_1);// 现在boundFunc是一个可调用对象它可以在稍后调用并且会调用当前对象的memberFunctionboundFunc(42);}};intmain(){MyClass obj;obj.setupCallback();// 输出: Member function called with x 42 and this 对象的地址return0;}当你使用std::bind来创建一个指向成员函数的绑定时你需要显式地提供这个this指针因为std::bind无法自动获取它。这是因为在std::bind的调用时刻它并不知道你将如何或何时使用这个绑定所以它无法知道哪个对象实例应该被使用。本篇文章的分享就到这里了如果您觉得在本文有所收获还请留下您的三连支持哦~

相关新闻

快速掌握C#语言基础知识点(19.数组集合字典)

快速掌握C#语言基础知识点(19.数组集合字典)

关注我的动态 namespace _19.数组_集合和字典 {internal class Program{static void Main(string[] args){//1.数组的定义int[] ints { 1, 2, 3 };//数组一旦初始化为固定大小&#xff0c;无法动态增减成员//ints[ints.Length] 1;//这个会出错//2.列表的定义List<int> …

2026/10/7 11:58:53 阅读更多 →
10个实战案例:教你用zero-shot-classify-SSTuning-base解决文本分类难题

10个实战案例:教你用zero-shot-classify-SSTuning-base解决文本分类难题

10个实战案例&#xff1a;教你用zero-shot-classify-SSTuning-base解决文本分类难题 【免费下载链接】zero-shot-classify-SSTuning-base 项目地址: https://ai.gitcode.com/hf_mirrors/IIC/zero-shot-classify-SSTuning-base zero-shot-classify-SSTuning-base是达摩院…

2026/9/25 4:03:43 阅读更多 →
【限时开放】20年ML架构师私藏学习地图首次公开:覆盖数学→编码→部署→调优全链路,仅剩83份完整版可下载

【限时开放】20年ML架构师私藏学习地图首次公开:覆盖数学→编码→部署→调优全链路,仅剩83份完整版可下载

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;AI学机器学习 机器学习是人工智能的核心支柱&#xff0c;它赋予系统从数据中自动学习规律并做出预测或决策的能力。对初学者而言&#xff0c;理解“AI学机器学习”并非指AI在自主学习&#xff0c;而是人类借助…

2026/9/27 6:50:46 阅读更多 →

最新新闻

日系扁平插画动态边缘光管线:让轮廓光变成会动的色块

日系扁平插画动态边缘光管线:让轮廓光变成会动的色块

日系扁平风格插画&#xff0c;和边缘光&#xff0c;这两个词放在一起&#xff0c;在过去很多年都是“水火不容”的。做日系的人觉得边缘光是厚涂和赛博朋克的专利&#xff0c;往平涂里一放&#xff0c;整个画面立刻变得油腻、廉价&#xff0c;那股清爽干净的劲儿全没了。我有段…

2026/10/7 12:34:34 阅读更多 →
Flutter钱包SDK鸿蒙适配:ed25519_hd_key踩坑与解决

Flutter钱包SDK鸿蒙适配:ed25519_hd_key踩坑与解决

上个月我把公司的钱包SDK往鸿蒙适配时&#xff0c;第一个挡路的组件不是UI&#xff0c;不是状态管理&#xff0c;而是ed25519_hd_key这个平时几乎没人注意的加密依赖。它在Flutter日志里安安静静地报了一串Unhandled Exception&#xff0c;一查&#xff0c;是SecureRandom初始化…

2026/10/7 12:34:34 阅读更多 →
实时AI后端架构:WebSocket心跳与异步工具调用实战

实时AI后端架构:WebSocket心跳与异步工具调用实战

1. 从一条热搜说起&#xff1a;实时 AI 到底在“实时”什么Gemini Live Avatar 刚出来那阵子&#xff0c;我朋友圈里做 AI 应用的人几乎都在转。大部分人第一反应是“哦&#xff0c;数字人又升级了”&#xff0c;然后划走。但我盯着它看了很久&#xff0c;因为我发现大家把注意…

2026/10/7 12:34:34 阅读更多 →
智能工厂数据底座实战:Linux实时内核与数据库分层存储

智能工厂数据底座实战:Linux实时内核与数据库分层存储

1. 智能工厂的底层逻辑&#xff1a;为什么是Linux在跑1.1 从一条产线的停机说起前两年我参与过一个汽车零部件工厂的数字化改造项目&#xff0c;产线上有一台关键设备突然停了&#xff0c;整条线跟着瘫了将近四十分钟。事后复盘&#xff0c;问题出在上位机操作系统上——那台机…

2026/10/7 12:34:34 阅读更多 →
MCP配置同步与Token优化:Claude Code与Cursor共享源配置实践

MCP配置同步与Token优化:Claude Code与Cursor共享源配置实践

1. 手动维护 MCP 配置这件事&#xff0c;到底卡在哪儿如果你同时用 Claude Code 和 Cursor&#xff0c;又刚好在项目里接了两三个 MCP Server&#xff0c;那你大概率经历过这个场景&#xff1a;在 Claude Code 的配置文件里写完一遍mcpServers&#xff0c;转头打开 Cursor&…

2026/10/7 12:34:34 阅读更多 →
Winform 窗体控件缩放自适应:辅助类实现原理与避坑指南

Winform 窗体控件缩放自适应:辅助类实现原理与避坑指南

简介&#xff1a;适用于Winform开发的窗体与控件布局缩放自适应辅助类&#xff0c;面向使用C#进行桌面应用开发、需要处理不同分辨率下界面适配问题的开发者。该辅助类支持对Winform自带多数控件及自定义控件进行缩放&#xff0c;可动态添加控件并保留自适应特性&#xff0c;同…

2026/10/7 12:33:33 阅读更多 →

日新闻

ROS2机械臂仿真与运动控制:从URDF建模到Gazebo实战全解析

ROS2机械臂仿真与运动控制:从URDF建模到Gazebo实战全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:01:58 阅读更多 →
用浏览器直接改ESP32的WiFi密码:NVS键值配置工具设计与实现

用浏览器直接改ESP32的WiFi密码:NVS键值配置工具设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:02:00 阅读更多 →
芯片封装缺陷检测:扫描声学显微镜(SAT)原理与实操指南

芯片封装缺陷检测:扫描声学显微镜(SAT)原理与实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:02:00 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 7:15:40 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 5:29:09 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 9:29:10 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 8:21:32 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 11:43:46 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 1:18:13 阅读更多 →