【C++】模板初阶: 解析模板原理、实例化与特化
相关专栏【Linux专栏】【C语言专栏】【测试专栏】【MySQL专栏】【C 专栏】 相关文章推荐【C】STL从零掌握STL容器特性与实战用法【C】C类与对象2C构造函数、运算符重载与流输入输出全面解析【测试】一文吃透软件测试全分类入门必懂核心体系【Linux】一文搞懂HTTP协议概念、报文格式与极简服务器实现很高兴你点开这篇文章✨这里会持续更新我喜欢的内容关注我一起慢慢变好呀 点赞 ⭐ 收藏 评论文章目录前言一、函数模板1.1 为什么需要函数模板1.2 函数模板的语法1.3 模板的使用二、模板参数推导与实例化2.1 隐式实例化2.2 参数不匹配时的处理2.3 多类型模板参数2.4 显式实例化三、模板重载与匹配规则3.1 函数模板与普通函数可以重载3.2 匹配优先级四、类模板4.1 类模板的定义4.2 类模板成员函数的类外定义4.3 类模板的使用显式实例化五、模板的注意事项5.1 声明与定义分离的问题5.2 模板的编译原理5.3 模板的缺点六、完整示例通用Stack类七、知识点汇总八、常见面试题前言在C语言中如果我们要写一个交换两个整数的函数再写一个交换两个浮点数的函数我们只能写两个不同名的函数或者用宏但宏有很多坑。C提供了模板来解决这个问题。有了模板我们就可以写出类型相关的通用代码。这一篇我们来学习函数模板如何写一个通用的Swap函数模板参数推导编译器如何自动推断类型显式实例化强制指定模板参数类型类模板如何写一个通用的Stack容器 ✨ 一、函数模板1.1 为什么需要函数模板看看这个例子我们需要交换两个变量的值但int、double、char都需要写一个版本。// 代码重复严重voidSwap(intleft,intright){inttempleft;leftright;righttemp;}voidSwap(doubleleft,doubleright){doubletempleft;leftright;righttemp;}voidSwap(charleft,charright){chartempleft;leftright;righttemp;}函数模板// 一个模板搞定所有类型templatetypenameTvoidSwap(Tleft,Tright){T templeft;leftright;righttemp;}1.2 函数模板的语法// template 关键字 typename T 或 class TtemplatetypenameTvoidSwap(Tx,Ty){T tmpx;xy;ytmp;}// 多个模板参数templatetypenameT1,typenameT2voidfunc(constT1x,constT2y){// ...}注意typename和class在模板参数中完全等价没有区别。templatetypenameT// 推荐更语义化templateclassT// 也可以C早期用法1.3 模板的使用intmain(){inti1,j2;doublem1.1,n2.2;Swap(i,j);// 编译器推导 T intSwap(m,n);// 编译器推导 T double// Swap(i, n); // 错误T被推导成int还是double矛盾return0;} ✨ 二、模板参数推导与实例化2.1 隐式实例化编译器会根据你传入的实参类型自动推导模板参数templatetypenameTTAdd(constTleft,constTright){returnleftright;}intmain(){inta110,a220;doubled110.1,d220.2;Add(a1,a2);// 隐式实例化T → intAdd(d1,d2);// 隐式实例化T → doublereturn0;}2.2 参数不匹配时的处理编译器根据你传入的实参类型自动推导模板参数Add(a1,d1);// 错误T被推导成int还是double解决方法1强制类型转换coutAdd(a1,(int)d1)endl;// 都转成intcoutAdd((double)a1,d1)endl;// 都转成double解决方法2显式实例化推荐coutAddint(a1,d1)endl;// 明确指定 T intcoutAdddouble(a1,d1)endl;// 明确指定 T double2.3 多类型模板参数templatetypenameT1,typenameT2T1Add(constT1left,constT2right){returnleftright;}intmain(){inta110;doubled120.2;coutAdd(a1,d1)endl;// T1int, T2double返回intreturn0;}2.4 显式实例化templatetypenameTT*func1(intn){returnnewT[n];}intmain(){// 无法推导T必须显式指定int*p1func1int(10);// T → intdouble*p2func1double(10);// T → doubledelete[]p1;delete[]p2;return0;} ✨ 三、模板重载与匹配规则3.1 函数模板与普通函数可以重载// 函数模板templatetypenameTTAdd(constTleft,constTright){couttemplate Add: ;returnleftright;}// 普通函数特化版本intAdd(constintx,constinty){coutnormal Add: ;return(xy)*10;}intmain(){inta110,a220;coutAdd(a1,a2)endl;// 输出normal Add: 300// 普通函数优先级更高coutAddint(a1,a2)endl;// 输出template Add: 30// 显式指定强制调用模板doubled11.1,d22.2;coutAdd(d1,d2)endl;// 输出template Add: 3.3// 没有匹配的普通函数调用模板return0;}3.2 匹配优先级优先级匹配规则1最高完全匹配的普通函数2通过模板实例化得到匹配函数3最低通过类型转换匹配 ✨ 四、类模板4.1 类模板的定义templatetypenameTclassStack{public:Stack(intn4):_array(newT[n]),_size(0),_capacity(n){}~Stack(){delete[]_array;_arraynullptr;_size_capacity0;}voidPush(constTx);private:T*_array;size_t _capacity;size_t _size;};4.2 类模板成员函数的类外定义关键类外定义时需要加上templatetypename T并用类名T::指定作用域。// 类外定义成员函数templatetypenameTvoidStackT::Push(constTx){if(_size_capacity){// 扩容逻辑T*tmpnewT[_capacity*2];memcpy(tmp,_array,sizeof(T)*_size);delete[]_array;_arraytmp;_capacity*2;}_array[_size]x;}4.3 类模板的使用显式实例化注意类模板不支持隐式实例化必须显式指定模板参数类型。intmain(){// 显式实例化指定T为intStackintst1;st1.Push(1);st1.Push(2);st1.Push(3);// 显式实例化指定T为doubleStackdoublest2;st2.Push(1.1);st2.Push(2.2);st2.Push(3.3);// 动态分配类模板对象Stackdouble*pstnewStackdouble;pst-Push(10.5);deletepst;return0;} ✨ 五、模板的注意事项5.1 声明与定义分离的问题模板的声明和定义通常不能分离到.h和.cpp文件中。// Stack.htemplatetypenameTclassStack{public:voidPush(constTx);};// Stack.cpp 错误链接时会找不到定义templatetypenameTvoidStackT::Push(constTx){/* ... */}解决办法将定义直接写在.h文件中或者在.cpp文件末尾显式实例化需要的类型// Stack.cpp - 显式实例化templateclassStackint;templateclassStackdouble;5.2 模板的编译原理模板在编译阶段根据使用情况生成具体代码编译器看到模板定义时不会生成代码编译器看到实例化如Stackint时才会生成对应的类代码不同的实例化生成不同的类Stackint和Stackdouble是不同类型5.3 模板的缺点缺点说明编译慢每次实例化都要重新生成代码代码膨胀不同类型生成多份代码错误信息复杂模板编译错误信息难以阅读声明定义难分离通常只能写在头文件 ✨ 六、完整示例通用Stack类#includeiostream#includestringusingnamespacestd;templatetypenameTclassStack{public:Stack(intn4):_array(newT[n]),_size(0),_capacity(n){coutStack()endl;}~Stack(){delete[]_array;_arraynullptr;_size_capacity0;cout~Stack()endl;}voidPush(constTx){if(_size_capacity){Expand();}_array[_size]x;}voidPop(){if(_size0)_size--;}TTop(){return_array[_size-1];}boolEmpty()const{return_size0;}size_tSize()const{return_size;}private:voidExpand(){T*tmpnewT[_capacity*2];for(size_t i0;i_size;i){tmp[i]_array[i];}delete[]_array;_arraytmp;_capacity*2;}T*_array;size_t _capacity;size_t _size;};intmain(){// int栈StackintintStack;intStack.Push(10);intStack.Push(20);intStack.Push(30);while(!intStack.Empty()){coutintStack.Top() ;intStack.Pop();}coutendl;// 30 20 10// string栈StackstringstrStack;strStack.Push(hello);strStack.Push(world);coutstrStack.Top()endl;// worldreturn0;} ✨ 七、知识点汇总知识点核心要点函数模板template 函数定义模板参数typename和class完全等价隐式实例化编译器自动推导参数类型显式实例化FuncName(a, b)强制指定类模板必须显式实例化如Stack类外定义需template Stack::匹配优先级普通函数 模板实例化 类型转换编译特性模板在实例化时才生成代码 ✨ 八、常见面试题Q1typename和class在模板中有什么区别在模板参数中完全等价。但typename还可以用于嵌套依赖类型例如typename T::iterator。Q2函数模板可以隐式实例化类模板为什么不行函数模板编译器可以实参推导类模板没有推导依据构造函数实参可以推导C17开始支持。C17开始类模板也支持部分隐式推导CTAD。Q3模板声明和定义为什么不能分离模板在实例化时才生成代码。如果定义在.cpp文件其他文件包含.h时看不到定义无法实例化导致链接错误。Q4模板代码膨胀怎么解决将不依赖模板参数的公共代码抽取到基类或单独的函数中。下一篇我们来学习STL初识vector、list、map等迭代器的使用 ✨ 谢谢你看到这里呀如果喜欢这篇内容点个关注下次更新不迷路✨ 点赞 ⭐ 收藏 评论

相关新闻

什么ai可以生成word文档 AI导出鸭导出稳得一批

什么ai可以生成word文档 AI导出鸭导出稳得一批

结构化数据突围:AI生成Word文档的工程化测评与架构方案 一、痛点:当大模型遇见Office,谁在制造“数字垃圾”? 在过去18个月的企业AI落地实践中,一个高频但被严重低估的问题浮出水面:AI生成的Word文档&#…

2026/6/26 21:25:48阅读更多 →
MC9S12XE外部总线接口(XEBI)配置、时序与调试全解析

MC9S12XE外部总线接口(XEBI)配置、时序与调试全解析

1. 项目概述与核心价值在嵌入式系统,尤其是汽车电子和工业控制这类对实时性与可靠性要求极高的领域,微控制器(MCU)的内置资源(如Flash、RAM)往往不足以支撑复杂的应用。这时,外部总线接口&#…

2026/6/27 11:52:30阅读更多 →
OpenCore Legacy Patcher终极指南:让老旧Mac免费升级最新macOS系统

OpenCore Legacy Patcher终极指南:让老旧Mac免费升级最新macOS系统

OpenCore Legacy Patcher终极指南:让老旧Mac免费升级最新macOS系统 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 你是否还在为苹果官方停止支持…

2026/6/27 23:35:58阅读更多 →
RA6W1 MCU低功耗模式深度解析:DPM与Sleep 3配置实战

RA6W1 MCU低功耗模式深度解析:DPM与Sleep 3配置实战

1. 项目概述:为什么RA6W1的DPM低功耗模式值得深究 在物联网设备开发中,功耗控制是决定产品成败的关键因素之一。一个需要常年靠电池供电的传感器,或者一个部署在难以取电位置的网关,其续航能力直接关系到维护成本和用户体验。瑞萨…

2026/6/28 13:28:56阅读更多 →
深入解析瑞萨RA8M1 MCU硬件架构:从Cortex-M85内核到低功耗与安全设计实战

深入解析瑞萨RA8M1 MCU硬件架构:从Cortex-M85内核到低功耗与安全设计实战

1. 项目概述与核心价值在嵌入式开发领域,选对一颗MCU,项目就成功了一半。瑞萨电子的RA8M1,作为其RA8系列基于Arm Cortex-M85内核的旗舰级32位MCU,自发布以来就因其高达480MHz的主频、强大的DSP/ML扩展指令集以及内置的Helium技术&…

2026/6/28 13:28:56阅读更多 →
瑞萨RX微控制器TSIP硬件安全引擎:从HMAC验证到TLS证书处理的嵌入式实践

瑞萨RX微控制器TSIP硬件安全引擎:从HMAC验证到TLS证书处理的嵌入式实践

1. 项目概述与TSIP模块核心价值在嵌入式开发,尤其是涉及物联网终端、工业控制器或智能电表这类需要联网或远程管理的设备时,安全不再是“加分项”,而是“及格线”。数据在传输过程中被篡改、设备身份被仿冒、固件被非法升级,任何一…

2026/6/28 13:28:56阅读更多 →
瑞萨RX系列TSIP硬件加密模块实战:AES-CMAC、DES与ARC4 API深度解析

瑞萨RX系列TSIP硬件加密模块实战:AES-CMAC、DES与ARC4 API深度解析

1. 项目概述在嵌入式开发,尤其是物联网和工业控制领域,数据安全已经从“加分项”变成了“必选项”。无论是设备间的通信指令,还是存储在Flash中的固件,一旦被篡改或窃听,轻则功能异常,重则引发安全事故。对…

2026/6/28 13:28:56阅读更多 →
瑞萨RX TSIP硬件安全模块性能深度解析与嵌入式实战指南

瑞萨RX TSIP硬件安全模块性能深度解析与嵌入式实战指南

1. 项目概述:RX TSIP模块的嵌入式安全实战在嵌入式开发,尤其是物联网和工业控制领域,安全不再是“锦上添花”,而是“生死攸关”的底线。我经历过太多项目,初期为了赶进度用软件实现AES或RSA,结果要么性能瓶…

2026/6/28 13:28:56阅读更多 →
RA8M1低功耗定时器(AGT/ULPT)原理、配置与物联网低功耗设计实践

RA8M1低功耗定时器(AGT/ULPT)原理、配置与物联网低功耗设计实践

1. 项目概述:RA8M1低功耗定时器的核心价值在嵌入式开发领域,尤其是面向物联网终端、便携式医疗设备、智能传感器等电池供电场景,功耗是决定产品生命周期的关键。这类设备大部分时间处于休眠状态,但需要维持基础的计时功能&#xf…

2026/6/28 13:23:56阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/6/28 0:08:01阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/6/28 0:08:01阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/6/28 0:08:01阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/6/28 0:08:01阅读更多 →