当前位置: 首页 > article >正文

C++:模板精讲

泛型编程当我们实现一个交换函数想要实现不同类型的交换可以使用函数重载#includeiostreamusing namespace std;voidSwap(intleft,intright){inttempleft;leftright;righttemp;}voidSwap(charleft,charright){chartempleft;leftright;righttemp;}voidSwap(doubleleft,doubleright){doubletempleft;leftright;righttemp;}但是这样有几个不好的地方重载的函数仅仅是类型不同代码复用率较低只要有新类型出现就需要用户自己增加对应的函数。代码的可维护性比较低一个出错可能所有的重载均出错。所以在C中就出现了模板这个概念编写与类型无关的代码是代码复用的一种基础模板是泛型编程的基础。函数模板函数模板代表了一个函数家族该函数模板与类型无关在使用时被参数化根据实参类型产生函数的特定类型版本。被参数化的这个过程叫做函数模板的示例化。函数模板的格式templatetypenameT1,typenameT2即返回类型 函数名参数列表{}#includeiostreamusingnamespacestd;templatetypenameTvoidSwap(Tleft,Tright){T templeft;leftright;righttemp;}intmain(){charax;charbc;Swap(a,b);couta bendl;return0;}注意typename 是用来定义模板参数关键字也可以使用class切记不能使用struct代替class。typename和class 只有一种情况不能等价比如 T::iterator 前面只能用 typename说明这是一个类型templateclassTvoidfunc(){// 这里必须写 typename不能写 classtypenameT::iterator it;}函数模板的原理函数模板是一个标准复制件它本身并不是函数是编译器用使用方式产生特定具体类型函数的摸具。所以模板就是将本来我们要重复做的事交给了编译器。在编译器阶段对于模板函数的使用编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。函数模板的实例化用不同类型的参数使用函数模板时称为函数模板的实例化。函数模板实例化分为隐式实例化和显式实例化隐式实例化#includeiostreamusingnamespacestd;templateclassTTAdd(constTleft,constTright){returnleftright;}intmain(){inta110,a220;doubleb15.2,b213.14;coutAdd(a1,a2)endl;coutAdd(b1,b2)endl;return0;}显式实例化#includeiostreamusingnamespacestd;templateclassTTAdd(constTleft,constTright){returnleftright;}intmain(){inta10;doubleb20.0;//显示实例化coutAddint(a,b)endl;return0;}注意如果在实例化中类型不匹配编译器一般不会进行类型转化操作因为一旦转化出现问题编译器就需要背锅。模板参数的匹配机制1.一个非模板函数可以和一个同名的函数模板同时存在而且该函数模板还可以被示例化为这个非模板函数。#includeiostreamusingnamespacestd;intAdd(intleft,intright){returnleftright;}templateclassTTAdd(constTleft,constTright){returnleftright;}intmain(){Add(1,2);Addint(1,2);}2.对于非模板函数和同名函数模板如果其他条件都相同在调动时会优先调动非模板函数而不会从该模板产生一个实例。如果模板可以产生一个具有更好匹配的函数那么选择该模板。#includeiostreamusingnamespacestd;intAdd(intleft,intright){returnleftright;}templateclassT1,classT2T1Add(constT1left,constT2right){returnleftright;}intmain(){Add(1,2);Add(1,2.0);return0;}模板函数不允许自动类型转化但是普通函数可以进行自动类型转化。类模板类模板的定义格式templateclassT1,clasT2,......classTnclass类模板名{//类内成员变量}#includeiostreamusingnamespacestd;templatetypenameTclassStack{public:Stack(size_t capacity4){_arraynewT[capacity];_capacitycapacity;_size0;}private:T*_array;size_t _capacity;size_t _size;};intmain(){Stackintst1;Stackdoublest2;return0;}类模板的实例化类模板的实例化和函数模板的实例化不同类模板实例化需要在类模板名字后跟 然后将实例化的类型放在中即可类模板名字不是真正的类而实例化的结果才是真正的类。非类型模板参数模板参数分类类型形参和非类型形参类型形参出现在模板参数列表中跟在class 或者typename之类的参数类型名称之后。非类型形参就是用一个常量作为类函数模板的一个参数在类函数模板中可以将该参数当成常量来使用。#includeiostreamusingnamespacestd;namespaceyjjh{templateclassT,size_t N10classarray{public:array(){for(inti0;iN;i){_array[i]T();}_sizeN;}Toperator[](size_t index){return_array[index];}constToperator[](size_t index)const{return_array[index];}size_tsize()const{return_size;}boolempty()const{return0_size;}private:T _array[N];size_t _size;};}intmain(){yjjh::arrayint,5a1;couta1[4]endl;return0;}注意浮点数类对象以及字符串是不允许作为非类型模板参数的浮点数在计算机里是二进制近似存储模板要求不同值必须生成不同类浮点数因为精度问题做不到绝对安全区分类对象太复杂有构造、析构、成员变量编译器无法在编译期把一个完整对象编码进类型字符串是数组 / 指针地址不确定编译器无法把不确定地址当作模板常量非类型模板参数必须在编译期间就确认结果模板的特化通常情况下使用模板可以实现一些与类型无关的代码但对于一些特殊类型的可能会得到一些错误的结果需要特殊处理比如实现了一个专门用来进行小于比较的函数模板。#includeiostreamusingnamespacestd;classDate{public:Date(intyear1900,intmonth1,intday1){_yearyear;_monthmonth;_dayday;}booloperator(constDated2){if(_yeard2._year){returnfalse;}elseif(_yeard2._year){if(_monthd2._month){returnfalse;}elseif(_monthd2._month){return_dayd2._day;}}returntrue;}private:int_year;int_month;int_day;};templateclassTboolLess(T left,T right){returnleftright;}intmain(){coutLess(1,2)endl;Dated1(2026,4,23);Dated2(2025,6,7);coutLess(d1,d2)endl;coutLess(p1,p2)endl;return0;}可以看到Less大多数情况下都可以正常比较但是在特殊场景下就得到错误的结果。上述示例中p1指向的d1显然小于p2指向的d2对象但是Less内部没有比较p1和p2指向的对象内容而比较的是p1 和 p2 指针的地址这就无法到达预期而错误。所以我们需要模板特化模板特化模板特化在原模板类的基础上针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。函数模板特化步骤1.必须要先有基础的函数模板2.关键字template后面接一对尖括号3.函数名后跟一对尖括号尖括号中指定需要特化的类型4.函数形参表必须要和模板函数的基础函数类型完全相同如果不同编译器可能会报一些奇怪的错误#includeiostreamusingnamespacestd;classDate{public:Date(intyear1900,intmonth1,intday1){_yearyear;_monthmonth;_dayday;}booloperator(constDated2){if(_yeard2._year){returnfalse;}elseif(_yeard2._year){if(_monthd2._month){returnfalse;}elseif(_monthd2._month){return_dayd2._day;}}returntrue;}private:int_year;int_month;int_day;};templateclassTboolLess(T left,T right){returnleftright;}//对Less模板进行特化templateboolLessDate*(Date*left,Date*right){return*left*right;}intmain(){coutLess(1,2)endl;Dated1(2026,4,23);Dated2(2025,6,7);coutLess(d1,d2)endl;Date*p1d1;Date*p2d2;coutLess(p1,p2)endl;return0;}注意一般情况下如果函数模板遇到不能处理或者处理有误的类型为了实现简单通常都是将该函数直接给出。该实现简单明了对于一些参数类型复杂的函数模板特化后代码可读性不强。类模板特化必须要先有基础的函数模板关键字template后面接一对尖括号类名后面接一对尖括号填上类型全特化全特化是将模板参数列表中所有的参数都确定化#includeiostreamusingnamespacestd;templateclassT1,classT2classData{public:Data(){coutDataT1,T2endl;}private:T1 _d1;T2 _d2;};templateclassDataint,char{public:Data(){coutDataT1,T2endl;}private:int_d1;char_d2;};偏特化偏特化任何针对模板参数进一步进行条件限制设计的特化版本。1.部分特化usingnamespacestd;templateclassT1,classT2classData{public:Data(){coutDataT1,T2endl;}private:T1 _d1;T2 _d2;};templateclassT1classDataT1,int{public:Data(){coutDataT1,intendl;}private:T1 _d1;int_d2;};2.参数更进一步的限制#includeiostreamusingnamespacestd;templateclassT1,classT2classData{public:Data(){coutDataT1,T2endl;}private:T1 _d1;T2 _d2;};templatetypenameT1,typenameT2classDataT1*,T2*{public:Data(){coutDataT1*,T2*endl;}private:T1 _d1;T2 _d2;};templatetypenameT1,typenameT2classDataT1,T2{public:Data(){coutDataT1,T2endl;}private:T1 _d1;T2 _d2;};类模板特化的应用有如下专门按照小于补交的类模板Less:#includeiostream#includevector#includealgorithmusingnamespacestd;classDate{friendostreamoperator(ostreamout,constDated);public:Date(intyear1900,intmonth1,intday1){_yearyear;_monthmonth;_dayday;}booloperator(constDated2)const{if(_yeard2._year){returnfalse;}elseif(_yeard2._year){if(_monthd2._month){returnfalse;}elseif(_monthd2._month){return_dayd2._day;}}returntrue;}private:int_year;int_month;int_day;};ostreamoperator(ostreamout,constDated){outd._year-d._month-d._day;returnout;}templateclassTstructLess{booloperator()(constTx,constTy)const{returnxy;}};模板的分离编译假如有一下场景模板的声明与定义分离开在头文件中进行声明在源文件中完成定义会直接报错// a.htemplateclassTvoidfunc(T a);// 声明// a.cpptemplateclassTvoidfunc(T a){}普通函数 / 类编译.cpp就生成具体机器码声明放头文件、定义放源文件支持分离编译。模板函数 / 类本质是代码蓝图没有具体类型不会主动生成实体代码。模板只有在 被指定类型使用实例化 时才会替换类型、生成对应代码。若分离编译模板定义写在.cpp外部文件只引入头文件声明编译各自文件时两边都没法实例化生成代码链接阶段找不到函数实体报错。解决方法将声明和定义放到一个.h文件中模板定义的位置显示实例化不推荐使用模板总结优点模板复用了代码节省资源更快的迭代开发增强了代码的灵活性缺点模板会导致代码膨胀问题也会导致编译时间变长出现模板编译错误时错误信息非常凌乱不一定位错误

相关文章:

C++:模板精讲

泛型编程 当我们实现一个交换函数&#xff0c;想要实现不同类型的交换&#xff0c;可以使用函数重载&#xff1a; #include<iostream>using namespace std;void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swap(char& …...

015-016 类中方法中的this,解决类中this指向问题

类中方法中的this<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…...

Weka回归分析实战:从数据预处理到模型部署

1. 项目概述&#xff1a;Weka中的回归机器学习实战指南在数据科学领域&#xff0c;回归分析是预测连续型变量的经典方法。Weka作为一款开源的机器学习工作台&#xff0c;以其友好的图形界面和丰富的算法库&#xff0c;成为许多从业者快速验证模型的首选工具。不同于Python/R需要…...

边缘节点的PHP应用部署、数据同步、算力调度标准化方案=hyperf最

针对边缘节点场景&#xff0c;按三个维度给你整理最实用的 Hyperf 方案&#xff1a;---一、应用部署标准化 容器化 & 打包 …...

ARM智能卡接口(SCI)架构与通信协议详解

1. ARM智能卡接口(SCI)核心架构解析 智能卡接口(Smart Card Interface, SCI)作为嵌入式系统中实现安全通信的关键模块&#xff0c;其硬件架构设计直接决定了系统与智能卡之间的通信效率和可靠性。ARM架构下的SCI模块采用分层设计理念&#xff0c;主要由物理层、协议层和应用层组…...

别再手动算了!用Matlab的dec2hex/dec2bin函数搞定进制转换(附硬件寄存器操作实例)

别再手动算了&#xff01;用Matlab的dec2hex/dec2bin函数搞定进制转换&#xff08;附硬件寄存器操作实例&#xff09; 在嵌入式开发和数字电路设计中&#xff0c;进制转换是工程师们每天都要面对的"家常便饭"。想象一下这样的场景&#xff1a;你正在调试一块FPGA板卡…...

evolver部署教程:构建自动优化AI系统

在运行进化算法或自动优化类 AI 系统时&#xff0c;计算资源与运行稳定性会直接影响结果质量。尤其是在需要长时间迭代、批量实验或多轮计算的场景中&#xff0c;一些具备稳定资源与弹性能力的环境&#xff08;如莱卡云服务器这类部署方式&#xff09;通常更有利于实验持续推进…...

扩展拖垮VSCode?禁用这3类高危插件,启动速度提升3.2倍,实测有效

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;扩展拖垮VSCode&#xff1f;禁用这3类高危插件&#xff0c;启动速度提升3.2倍&#xff0c;实测有效 VSCode 启动缓慢常被误认为是硬件或系统问题&#xff0c;但真实瓶颈往往藏在插件生态中。我们对 127…...

如果openKylin 2.0 SP2主机的IPv4地址改变,如何让GitLab正常运行

作者&#xff1a;沈传越 明德融创工作室&#xff08;Minter Fusion Studio, MFS&#xff09; 出品 本文介绍的所有步骤均经过测试复现。 本文针对GitLab管理员使用。如果对于相关的专业词汇不太清楚。可以参考以下文章&#xff1a; 《如何在openKylin下安装并配置GitLab&…...

VSCode量子插件配置踩坑实录:92%开发者忽略的3项核心环境校验与自动修复方案

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;VSCode量子插件配置踩坑实录&#xff1a;92%开发者忽略的3项核心环境校验与自动修复方案 VSCode 量子开发插件&#xff08;如 Q# Extension、Quantum Development Kit&#xff09;在启用时频繁报错&…...

DimOS:AI原生机器人操作系统入门与实践指南

1. 项目概述&#xff1a;重新定义机器人操作系统如果你在过去几年里折腾过机器人开发&#xff0c;大概率绕不开ROS&#xff08;Robot Operating System&#xff09;。从ROS 1到ROS 2&#xff0c;它确实为机器人软件模块化、通信标准化立下了汗马功劳。但说实话&#xff0c;有多…...

告别STC-ISP!手把手教你写一个通吃STC89/12/15系列单片机的延时函数库

告别STC-ISP&#xff01;手把手教你打造跨代STC51单片机的延时函数库 当你在深夜调试STC89C52RC时&#xff0c;突然接到需求要移植代码到STC15W4K32S4上&#xff0c;却发现原本精准的延时函数完全失效——这种场景对51单片机开发者来说再熟悉不过。不同指令集架构带来的时钟周期…...

量子模拟中的N-可表示性问题与相关纯化方法

1. 量子模拟中的N-可表示性问题在量子化学计算中&#xff0c;约化密度矩阵&#xff08;Reduced Density Matrix, RDM&#xff09;是描述多电子系统量子态的核心工具。特别是二电子约化密度矩阵&#xff08;2-RDM&#xff09;&#xff0c;它包含了计算系统能量和各类物理性质所需…...

未来3年,这3个AI赛道已经定了

我最近一直在想一件事。Anthropic上个月的年化收入超过了OpenAI。很多人看到这条新闻&#xff0c;觉得不过是个财报数字。但我觉得这是一个信号——一个新产业正式成型的信号。这个产业叫AI编程。先说为什么是Anthropic&#xff0c;不是OpenAIOpenAI体量更大&#xff0c;名气更…...

3个颠覆性功能让Pearcleaner成为Mac系统清理必备神器

3个颠覆性功能让Pearcleaner成为Mac系统清理必备神器 【免费下载链接】Pearcleaner A free, source-available and fair-code licensed mac app cleaner 项目地址: https://gitcode.com/gh_mirrors/pe/Pearcleaner 你是否想过&#xff0c;为什么Mac电脑用久了会越来越慢…...

Linux服务器磁盘爆满?手把手教你用parted命令在线扩容/home分区(CentOS 8/9实战)

Linux服务器磁盘爆满&#xff1f;手把手教你用parted命令在线扩容/home分区&#xff08;CentOS 8/9实战&#xff09; 凌晨三点&#xff0c;监控系统突然发出刺耳的警报声——生产环境的/home分区使用率突破95%。作为运维工程师&#xff0c;这种场景再熟悉不过&#xff1a;应用日…...

差分放大器在高速信号链中的关键作用与设计实践

1. 差分放大器在高速信号链中的核心作用在现代无线通信和高速数据采集系统中&#xff0c;差分放大器扮演着信号调理的关键角色。这类器件通过独特的平衡架构&#xff0c;能够有效抑制共模噪声并显著降低偶次谐波失真。以THS4509为例&#xff0c;其1900MHz的带宽和6600V/s的压摆…...

NREL风速数据API参数详解:从wkt坐标到interval间隔,新手避坑指南

NREL风速数据API参数详解&#xff1a;从wkt坐标到interval间隔&#xff0c;新手避坑指南 当你在可再生能源或气象研究领域初次接触NREL风速数据API时&#xff0c;是否曾被那一长串参数列表搞得晕头转向&#xff1f;本文将化身你的私人参数解码器&#xff0c;带你深入理解每个配…...

神经形态硬件在强化学习机器人控制中的低功耗实践

1. 项目概述&#xff1a;神经形态硬件上的强化学习机器人控制去年在实验室调试Astrobee机器人时&#xff0c;我遇到了一个棘手的问题&#xff1a;传统GPU方案虽然能实现精确控制&#xff0c;但功耗高达200W&#xff0c;根本无法满足太空任务对能源的苛刻要求。这促使我开始探索…...

Pytest及相关测试工具实战指南

一个完整的例子&#xff0c;手把手教你从零开始使用Pytest&#xff0c;Pytest-cov&#xff0c;Pylint&#xff0c;flake8。 例子&#xff1a;银行账户系统 编写测试 -> 检查覆盖率 -> 做静态分析 -> 代码风格检查 第一部分&#xff1a;Pytest入门 - 从零到熟…...

PyTorch实现Transformer英法机器翻译系统

1. 从零构建Transformer模型&#xff1a;实现英法机器翻译系统 2017年&#xff0c;Transformer架构的提出彻底改变了序列到序列任务的处理方式。作为一名长期从事NLP开发的工程师&#xff0c;我将带您完整实现一个基于PyTorch的英法翻译Transformer模型。不同于简单调用现成库&…...

从零实现5大机器学习基础算法:Python代码与数学推导

1. 从零实现机器学习基础算法的必要性在机器学习领域&#xff0c;调用现成的库&#xff08;如scikit-learn&#xff09;固然方便&#xff0c;但真正理解算法本质的开发者都会选择自己动手实现一遍。这就像学习烹饪时&#xff0c;从切菜开始准备食材比直接使用预制菜更能掌握料理…...

从‘像素’到‘3D模型’:手把手拆解David Marr视觉四层描述,理解CV任务本质

从像素到三维世界&#xff1a;用David Marr视觉理论重构计算机视觉认知框架 1982年出版的《视觉计算理论》中&#xff0c;David Marr提出的视觉处理层次模型&#xff0c;至今仍是理解计算机视觉任务本质的黄金标准。这位将神经科学、心理学与计算机科学交叉融合的天才学者&…...

数字孪生AI赋能智慧社区:从概念到落地的全景指南

数字孪生AI赋能智慧社区&#xff1a;从概念到落地的全景指南 引言 在数字化转型浪潮下&#xff0c;智慧社区正从简单的设备联网迈向虚实融合的智能新阶段。数字孪生&#xff08;Digital Twin&#xff09;与人工智能&#xff08;AI&#xff09;的结合&#xff0c;为社区治理、…...

AI Agent Harness日志体系:可追溯性设计

AI Agent Harness日志体系全解密:从零搭建全链路可追溯能力,让每一次Agent决策都有迹可循 关键词 AI Agent、Harness日志体系、可追溯性、全链路追踪、分布式日志、决策审计、故障根因分析 摘要 随着AI Agent从单场景原型落地到企业级多Agent协作生产系统,「决策黑盒」「…...

数字孪生AI赋能智慧商圈:从概念到落地的全解析

数字孪生AI赋能智慧商圈&#xff1a;从概念到落地的全解析 引言 在数字化转型浪潮下&#xff0c;传统的商业空间正经历一场深刻的智能化变革。数字孪生与人工智能的结合&#xff0c;为“智慧商圈”的构建提供了全新的技术范式。它不再仅仅是简单的线上地图或监控大屏&#xf…...

不用C、不用Verilog!用Ada点亮LED,这才是Zynq的“另一种打开方式”

当你还在用C语言写GPIO、用Verilog连LED的时候&#xff0c;有人已经开始用一门“冷门但强大”的语言——Ada&#xff0c;在Zynq上点灯了。1.1 设置 EMIO 允许PS控制 LED在 Zedboard 上&#xff0c;LED 只能通过可编程逻辑 (PL)&#xff08;FPGA&#xff09;端进行控制&#xff…...

港科夜闻|香港科大于THE亚洲大学排名2026位列第12位,彰显顶尖亚洲大学地位

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、在最新公布的2026年泰晤士高等教育&#xff08;THE&#xff09;亚洲大学排名中&#xff0c;香港科技大学位列亚洲第十二位&#xff0c;充分展现香港科大在蓬勃发展的亚洲高等教育界中站稳领先位置。作为一所扎根亚洲、放…...

统计学与机器学习:差异、融合与应用实践

1. 应用统计学与机器学习的紧密关系解析作为一名在数据科学领域工作多年的从业者&#xff0c;我经常被问到统计学和机器学习之间究竟有什么区别。简单来说&#xff0c;它们就像一对孪生兄弟——有着相同的基因却表现出不同的性格特征。统计学更注重理论严谨性和模型解释性&…...

港科大DeepTech 20| AI驱动的自动化智能正畸治疗方案设计系统

AI驱动的自动化智能正畸治疗方案设计系统 主要研究者&#xff1a;陈浩教授 技术成熟度&#xff1a;TRL 6技术成熟度&#xff08;Technology Readiness Level&#xff0c;TRL&#xff09;是一个用来评估技术方案从概念阶段到实际应用阶段的成熟程度和风险水平的系统方法&#xf…...