【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/19 23:02:34阅读更多 →
MC9S12XE外部总线接口(XEBI)配置、时序与调试全解析

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

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

2026/6/19 23:02:34阅读更多 →
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/19 23:02:34阅读更多 →
深入解析NXP S12XS Flash安全机制与高级内存操作命令

深入解析NXP S12XS Flash安全机制与高级内存操作命令

1. 项目概述与核心价值在嵌入式开发的深水区,Flash存储器的操作从来都不是简单的“写入”和“擦除”。它更像是一个戒备森严的保险库,而开发者就是那个需要掌握所有安全协议和应急钥匙的库管员。今天,我们就来深入拆解Freescale(现…

2026/6/20 0:12:40阅读更多 →
如何消除AI视频僵硬感:Singularity-LTX-2.3_OmniCine_V1终极指南

如何消除AI视频僵硬感:Singularity-LTX-2.3_OmniCine_V1终极指南

如何消除AI视频僵硬感:Singularity-LTX-2.3_OmniCine_V1终极指南 【免费下载链接】Singularity-LTX-2.3_OmniCine_V1 项目地址: https://ai.gitcode.com/hf_mirrors/WarmBloodAban/Singularity-LTX-2.3_OmniCine_V1 你是否厌倦了AI生成的视频总是显得生硬不…

2026/6/20 0:12:40阅读更多 →
3步搞定小说阅读广告净化:Legado替换规则实战指南

3步搞定小说阅读广告净化:Legado替换规则实战指南

3步搞定小说阅读广告净化:Legado替换规则实战指南 【免费下载链接】legado Legado 3.0 Book Reader with powerful controls & full functions❤️阅读3.0, 阅读是一款可以自定义来源阅读网络内容的工具,为广大网络文学爱好者提供一种方便、快捷舒适…

2026/6/20 0:12:40阅读更多 →
AI专著生成大揭秘!实用工具推荐,快速完成20万字专著创作

AI专著生成大揭秘!实用工具推荐,快速完成20万字专著创作

在首次迈入学术专著写作领域的研究者看来,这一过程就像是一场“摸索前行”的探索之旅,随处都是未知的挑战。选题上往往会感到困惑,不知道如何在“有意义”与“可行性”之间找到合适的平衡。有时候,选题过于宽泛,难以深…

2026/6/20 0:12:40阅读更多 →
如何高效提取微信聊天记录:开源本地化数据管理完整指南

如何高效提取微信聊天记录:开源本地化数据管理完整指南

如何高效提取微信聊天记录:开源本地化数据管理完整指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeCh…

2026/6/20 0:12:40阅读更多 →
MC68HC908低功耗模式与SPI通信:嵌入式系统节能与可靠通信设计

MC68HC908低功耗模式与SPI通信:嵌入式系统节能与可靠通信设计

1. 项目概述与核心价值在电池供电的嵌入式设备开发中,我们常常面临一个核心矛盾:设备需要时刻准备响应外部事件,但又必须尽可能省电以延长续航。解决这个矛盾的关键,就在于微控制器(MCU)的低功耗模式设计。…

2026/6/20 0:07:40阅读更多 →
【课程设计/毕业设计】基于 Web 的高校县志馆藏信息综合管理系统设计与实现 基于Django的青岛滨海学院特色文献捐赠流转管理系统的设计与实现【附源码、数据库、万字文档】

【课程设计/毕业设计】基于 Web 的高校县志馆藏信息综合管理系统设计与实现 基于Django的青岛滨海学院特色文献捐赠流转管理系统的设计与实现【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/6/20 0:02:40阅读更多 →
MC68HC908RF2A定时器PWM生成原理与实战:无缓冲与缓冲模式详解

MC68HC908RF2A定时器PWM生成原理与实战:无缓冲与缓冲模式详解

1. 项目概述与核心价值在嵌入式开发,尤其是电机驱动、LED调光、开关电源这些需要精确控制“能量”的领域,脉冲宽度调制(PWM)技术是工程师手中的一把瑞士军刀。它的本质很简单:用一个固定频率的方波,通过改变…

2026/6/20 0:02:40阅读更多 →
在银河麒麟V10桌面(2205版本)上实战部署软RAID 1:从模块黑名单到自动挂载

在银河麒麟V10桌面(2205版本)上实战部署软RAID 1:从模块黑名单到自动挂载

1. 银河麒麟V10桌面系统与软RAID 1基础认知 第一次在银河麒麟V10桌面上折腾软RAID 1时,我踩了不少坑。这个国产操作系统基于Linux内核,但2205版本对软RAID模块做了特殊处理,需要额外操作才能正常使用。软RAID 1其实就是磁盘镜像技术&#xff…

2026/6/20 0:02:40阅读更多 →