【C++】泛型编程详解:函数模板与类模板全梳理
目录泛型编程函数模板函数模板概念函数模板格式函数模板的原理函数模板的实例化1.隐式实例化让编译器根据实参推演模板参数的实际类型2.显式实例化在函数名后的中指定模板参数的实际类型模板参数的匹配原则类模板泛型编程泛型编程是一种编程思想核心是编写与类型无关的代码一份代码可以适配多种不同的数据类型不用为 int、char、自定义类等每种类型单独重写一套逻辑。C 里主要依靠模板来实现泛型编程分为函数模板和类模板。泛型编程和面向对象不同面向对象是通过继承和多态在运行时实现类型复用而泛型编程在编译期完成类型实例化。编译阶段编译器会根据使用的类型生成对应代码运行时不会有额外的类型判断开销效率更高。泛型编程最典型的应用就是 C 标准库 STL容器、算法、迭代器整套体系都是基于模板实现。sort 算法可以对数组、vector、list 等不同容器的不同类型数据排序就是泛型思想的体现。函数模板如何实现一个通用的交换函数呢void Swap(int left, int right) { int temp left; left right; right temp; } void Swap(double left, double right) { double temp left; left right; right temp; } void Swap(char left, char right) { char temp left; left right; right temp; }使用函数重载虽然可以实现但是有以下几个不好的地方1. 重载的函数仅仅是类型不同代码复用率比较低只要有新类型出现时就需要用户自己增 加对应的函数2. 代码的可维护性比较低一个出错可能所有的重载均出错。那能否告诉编译器一个模板让编译器根据不同的类型利用该模板来生成代码呢如果在C中也能够存在这样一个模具通过给这个模具中填充不同材料(类型)来获得不同材料的铸件(即生成具体类型的代码那将会节省许多头发。巧的是前人早已将树栽好我们只需在此乘凉。函数模板概念函数模板是 C 泛型编程里的工具可以理解成函数的模板模具它不是一个真实的函数只是告诉编译器一套通用代码逻辑类型暂时用参数代替。当代码调用这个模板的时候编译器会根据传入的数据类型自动生成对应类型的函数不用我们手动写多份重复代码。函数模板不会在编译时直接生成所有类型的函数只有当代码中用到这个模板编译器才会针对当前使用的类型生成对应函数这个过程叫做模板实例化。函数模板格式函数模板有两种写法typename 和 class 都可以用来声明模板类型参数效果相同。第一种用typenametemplatetypename T 返回值类型 函数名(参数列表) { 函数体 }第二种classtemplateclass T 返回值类型 函数名(参数列表) { 函数体 }template 是固定关键字代表这是模板尖括号里面是模板参数。T 是类型参数相当于类型占位符可以自己取名不一定要叫 T。如果有多个模板参数中间用逗号隔开。就拿swap函数举例templatetypename T void Swap(T left, T right) { T temp left; left right; right temp; }int main() { int a 3, b 5; double c 3.3, d 5.5; char ch1 e, ch2 f; Swap(a, b); Swap(c, d); Swap(ch1, ch2); cout a - b endl; cout c - d endl; cout ch1 - ch2 endl; return 0; }注意template 这一行必须写在函数前面不能和函数写在同一行。模板参数名字不能和普通变量重名。函数模板的原理函数模板本身不是可执行函数只是一份代码模板相当于给编译器的一套代码草稿。编译阶段编译器会根据调用处传入的实参类型自动推导类型生成对应类型的真实函数这个过程叫做模板实例化。当调用 Swapint或者直接传入 int 类型变量让编译器自动推导时编译器会拿模板里的 T 替换成 int生成一份 int 版本的 Swap 函数。如果后面调用时传入 double编译器再单独生成一份 double 版本的 Swap。每一种不同的类型都会产生一份独立的函数代码。模板不会在编译一开始就生成所有类型的函数只有代码中真正用到这个模板的时候编译器才会实例化出对应函数没有使用到的类型不会生成代码。这个特点叫做按需实例化。类型推导的时候如果模板参数无法确定编译就会报错。模板的语法检查分为两次第一次在定义模板的时候只检查基础语法第二次在实例化的时候结合具体类型检查类型相关逻辑。templatetypename T void Swap(T left, T right) { T temp left; left right; right temp; }int a 1, b 2; Swap(a, b); //编译器实例化得到下面这份函数 //void Swap(int left, int right) //{ // int temp left; // left right; // right temp; //} double x 1.1, y 2.2; Swap(x, y); //编译器实例化得到double版本的Swap函数简单总结函数模板的原理就是编译期根据不同类型复制替换模板代码生成多个重载函数这个工作由编译器自动完成不用程序员手动写多份重载。函数模板的实例化函数模板本身只是一段模板代码不是真正可以调用的函数。实例化就是编译器根据模板结合具体的类型参数生成一份真实函数代码的过程。1.隐式实例化让编译器根据实参推演模板参数的实际类型隐式实例化是函数模板实例化里最常用的方式。当代码中调用模板函数编译器会根据传入实参的类型自动推导模板参数 T然后用这个类型替换模板里所有的 T生成对应类型的真实函数整个过程不需要程序员手动指定类型。编译器在遇到函数调用时拿实参的类型匹配模板参数自动完成类型推导然后实例化出对应的函数。如果后续再用相同类型调用这个模板编译器不会重复生成函数直接复用之前已经生成好的代码。templatetypename T void Swap(T left, T right) { T temp left; left right; right temp; }int a 10, b 20; Swap(a, b);这里调用 Swap (a,b)实参 a 和 b 都是 int 类型编译器推导出 T 就是 int自动把模板里所有 T 替换成 int生成下面这个真实函数。void Swap(int left, int right) { int temp left; left right; right temp; }隐式实例化有几个关键点。第一只有当代码执行调用动作的时候编译器才会生成函数如果只写模板但全程没有调用不会产生任何实例代码。第二类型推导必须成功如果多个实参推导出的类型不一致编译就报错。第三同一个类型多次调用模板只会实例化一次不会重复生成多份相同函数。templateclass T T Add(const T x, const T y) { return x y; } int main() { int a1 10, a2 20; double d1 10.0, d2 20.0; //正常隐式实例化调用 Add(a1, a2); Add(d1, d2); //错误隐式实例化调用 Add(a1, d1); /* 该语句不能通过编译因为在编译期间当编译器看到该实例化时需要推演其实参类型 通过实参a1将T推演为int通过实参d1将T推演为double类型但模板参数列表中只有一个T 编译器无法确定此处到底该将T确定为int 或者 double类型而报错 注意在模板中编译器一般不会进行类型转换操作因为一旦转化出问题编译器就需要背黑锅 */ // 此时有两种处理方式1. 用户自己来强制转化 2. 使用显式实例化 Add(a1, (int)d1); return 0; }2.显式实例化在函数名后的中指定模板参数的实际类型显式实例化就是程序员主动告诉编译器模板要用什么类型强制编译器生成对应类型的函数就算代码里没有调用这个函数也会生成对应的函数实体。隐式实例化是调用的时候自动推导类型而显式实例化手动指定模板参数类型。语法格式template 返回值类型 函数名类型(参数列表);以swap函数举例templatetypename T void Swap(T left, T right) { T temp left; left right; right temp; } //显式实例化强制生成int版本Swap函数 template void Swapint(int, int);代码会让编译器把模板中的 T 全部替换成 int生成 int 版本的 Swap 函数。哪怕后面代码没有调用 Swapint这个函数代码依然会被生成到程序中。显式实例化的特点不需要调用函数就能生成函数实体。隐式实例化只有调用才会生成代码显式实例化写一句声明就直接生成。手动指定类型不依赖实参推导不会出现类型推导冲突的问题。一般用于多文件场景如果模板定义放在 cpp 文件在需要使用的地方显式实例化减少编译时间。显式实例化声明是用来生成函数实体的。还有一种是显式指定模板参数的调用写法这个不属于实例化声明只是调用函数int a1,b2; Swapint(a,b);#includeiostream using namespace std; templatetypename T void Swap(T left, T right) { T temp left; left right; right temp; } //显式实例化主动生成int版本函数 template void Swapint(int, int); //显式实例化主动生成char版本函数 template void Swapchar(char, char); int main() { //这里可以直接使用已经实例化好的函数 int x1,y2; Swap(x,y); return 0; }模板参数的匹配原则模板参数匹配的原则是优先进行实参类型推导编译器会根据函数调用传入的实参类型自动推导模板参数的类型这个过程会优先发生不需要我们手动指定模板参数。当实参可以成功推导出一致的类型时就会使用推导出来的类型实例化模板。如果同一个模板参数从不同实参推导出不一样的类型类型推导就会失败编译直接报错。当类型推导无法完成或者想要强制指定类型的时候可以在函数名后面的尖括号中显式给出模板参数手动指定的类型优先级高于自动推导。如果同时存在普通函数和同名的函数模板调用时会优先匹配普通函数只有普通函数不匹配的时候才会去匹配函数模板再进行实例化。如果函数模板可以实例化出和普通函数完全匹配的版本编译器依然优先选择普通函数不会使用模板实例化出来的函数。对于带多个模板参数的模板函数如果部分参数手动指定剩下的参数依旧可以尝试依靠实参推导得到。类模板类模板的定义需要以 template 关键字开头尖括号内部写模板参数之后再定义类模板参数相当于类内类型的占位符类里面的成员变量、成员函数都可以使用这个占位类型。基础格式如下。templatetypename T class 类名 { //类内可以使用T作为类型 T _member; public: //成员函数声明 函数返回值 函数名(参数); };如果存在多个模板参数在尖括号内用逗号隔开。templatetypename T1, typename T2 class 类名 { T1 _a; T2 _b; };代码实现了一个类模板 Stack能够创建存储任意类型元素的栈。templatetypename T是模板声明T 作为类型占位符。类里面的数组指针 T* _array代表数组存放元素的类型要等到实例化的时候才确定。类模板的成员函数写在类外的时候前面必须重新写 template 模板头类名必须写成 StackT告诉编译器这是属于 Stack 类模板的成员函数。#includeiostream using namespace std; // 类模版 templatetypename T class Stack { public: Stack(size_t capacity 4) { _array new T[capacity]; _capacity capacity; _size 0; } void Push(const T data); private: T* _array; size_t _capacity; size_t _size; }; //类外实现成员函数必须再加template模板头类名后面要写StackT templateclass T void StackT::Push(const T data) { //简单扩容判断如果容量满了就扩容这里简化写 if(_size _capacity) { size_t newcap _capacity * 2; T* tmp new T[newcap]; for(size_t i 0; i _size; i) { tmp[i] _array[i]; } delete[] _array; _array tmp; _capacity newcap; } _array[_size] data; _size; } int main() { Stackint st1; //实例化int版本的栈 Stackdouble st2; //实例化double版本的栈 st1.Push(10); st2.Push(3.14); return 0; }

相关新闻

二叉树最大深度:递归、DFS与BFS的三种解法深度解析

二叉树最大深度:递归、DFS与BFS的三种解法深度解析

刷 LeetCode Hot100 刷到第 28 题的时候,说实话我已经有点疲了,而这题《104. 二叉树的最大深度》看上去就是那种"白给"的简单题。但真把它掰开揉碎以后,我发现它其实是递归、DFS、BFS、分治思想的一块绝佳试金石,里面值…

2026/10/12 2:22:21 阅读更多 →
终端里的AI打砖块:用Bash实现Claude流式响应可视化

终端里的AI打砖块:用Bash实现Claude流式响应可视化

1. 项目概述:当AI助手变成终端里的复古游戏机最近在某开发者社区刷到一个标题特别扎眼的帖子:“Claude 干活的时候,在终端里打砖块”——第一反应是怀疑自己看错了。Claude 是那个以长上下文、强推理和文档理解见长的AI模型,不是用…

2026/10/12 2:22:21 阅读更多 →
mruby Amalgamation 单文件构建完全指南:原理、生成与嵌入实践

mruby Amalgamation 单文件构建完全指南:原理、生成与嵌入实践

编程语言语言运行时编译器解释器嵌入式 【免费下载链接】mruby Lightweight Ruby 项目地址: https://gitcode.com/gh_mirrors/mr/mruby 点击查看 免费下载 mruby 的 Amalgamation(合并构建)机制参照 SQLite 的分发模型,把整个运行…

2026/10/12 2:21:20 阅读更多 →

最新新闻

小白程序员必看:站在AI与业务“最后一公里”的FDE如何年入百万?

小白程序员必看:站在AI与业务“最后一公里”的FDE如何年入百万?

大模型落地总遇阻?FDE(前线部署工程师)是关键!本文解析FDE如何将AI能力转化为业务成果,澄清三大误解,详解“80/95/99”漏斗价值,拆解Echo-Delta双能力模型,提供教育、传媒、金融等四…

2026/10/12 5:38:19 阅读更多 →
豆包排版乱码全解析:从复制乱码到API编码一次讲透

豆包排版乱码全解析:从复制乱码到API编码一次讲透

最近几个群里陆续有人问我同一个问题:豆包生成的内容,复制到 Word 里全是井号、星号、竖线,页面上的正文直接显示成方块和问号,代码缩进乱成一团。我一看就知道,这些其实都不是同一个“乱码”,而是好几类问…

2026/10/12 5:38:19 阅读更多 →
OpenCV多目标匹配实战:微信连一连游戏图标精准定位

OpenCV多目标匹配实战:微信连一连游戏图标精准定位

1. 项目概述:为什么用OpenCV做“连一连”辅助不是炫技,而是工程上的合理选择“OpenCV制作微信小游戏最强连一连辅助(3)——matchTemplate多目标匹配”,这个标题里藏着三个关键信号:场景明确(微信…

2026/10/12 5:38:19 阅读更多 →
大模型 Tool Use 手写指南:原生调用、ReAct 与沙箱执行三种方案

大模型 Tool Use 手写指南:原生调用、ReAct 与沙箱执行三种方案

面试官让我手写一个 Tool Use。这句话我到现在都记得,因为在场的环境完全模拟真实办公:一个共享文档、一个编辑器,不允许查资料。Tool Use 听起来高大上,拆开看其实就是让大模型学会"伸手够"外部的数据源和函数&#xf…

2026/10/12 5:38:19 阅读更多 →
Cordis:AI原生应用的运行时契约架构解析

Cordis:AI原生应用的运行时契约架构解析

1. 项目概述:这不是一个“插件”,而是一套面向AI原生应用的运行时契约体系 “DeepSeek Harness 的 Cordis 插件架构”——光看这个名字,很多人第一反应是:“哦,又一个给大模型加功能的插件系统?”但我在某…

2026/10/12 5:38:19 阅读更多 →
AI Agent记忆层:用mem0构建跨会话的长期记忆系统

AI Agent记忆层:用mem0构建跨会话的长期记忆系统

1. 先说清楚:Agent缺的不是智商,是记性这两年做AI Agent项目的人应该都有同感:模型本身的推理能力已经很强了,真正拖后腿的反而是“记忆”。同一个用户第二次来提问,Agent完全不记得他上次说过什么;用户昨天…

2026/10/12 5:37:18 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 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/11 10:45:37 阅读更多 →
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/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练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/11 14:36:54 阅读更多 →