C++: 模板初阶
文章目录
- 一. 泛型编程
- 二. 函数模板
- 函数模板的原理
- 函数模板的实例化
- 隐式实例化: 让编译器根据实参推演模板参数的实际类型
- 显示实例化: 在函数名后的<>中制定模板参数的世纪类型
- 模板参数的匹配原则
- 三. 类模板
- 类模板的定义格式
- 类模板的实例化
一. 泛型编程
如何实现一个通用的交换函数呢? 参数是不同类型的数据.
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;
}
使用函数重载虽然可以实现, 但是仍然有不足:
- 重载的函数仅仅是类型不同, 代码复用率比较低, 只要有新类型出现时, 就需要用户自己增加对应的函数
- 代码的可维护性比较低, 一个出错可能所有的重载均出错.
如果需要能在用户提供的类型上使用此函数, 这种策略就失效了.
那么, 能否告诉编译器一个模子, 让编译器根据不同的类型利用该模子来生成代码呢?
就像活字印刷术一样, 只要提供了字模, 就可以用不同的颜色来印出同样的字

C++提供了模板的语法, 给这个"模具"中填充不同的类型, 就可以生成具体类型的代码
泛型编程: 编写与类型无关的通用代码, 是代码复用的一种手段. 模板是泛型编程的基础.

二. 函数模板
针对上面 Swap 函数有多种类型参数的情况, 可以定义一个通用的函数模板(function template), 而不是为每个类型都定义一个新函数.
一个函数模板就是一个公式, 可用来生成针对特定类型的函数版本. Swap 的模板版本可能像下面这样:
template <typename T> // 模板定义格式: template<typename T1, typename T2, ... , typename Tn>
void Swap(T &left, T &right)
{T temp = left;left = right;right = temp;
}
模板定义以关键字 template 开始, 后面跟一个模板参数列表, 这是一个用逗号分割的一个或多个模板参数的列表, 用小于号 < 和 大于号 > 包围起来.
在模板定义中, 模板参数列表不能为空.
模板参数列表的作用很像函数参数列表.
函数参数列表定义了形参对象, 模板参数列表定义了类型.
模板参数表示类或函数定义中用到的类型或值. 当使用模板的时候, 显示或隐式地制定模板实参, 将其绑定到模板参数上.
比如上述的 Swap 函数声明了两个名为 T 的类型参数, T 表示一个类型, T 的实际类型则在编译时根据传入的参数确定.
对于不同的参数类型, 最终调用的函数参数类型也不同

函数模板的原理
函数模板是一个蓝图, 它本身并不是函数, 是编译器用使用方式产生特定具体类型函数的模具. 所以其实模板就是将本来应该我们做得重复的事情交给了编译器.

在编译器编译阶段, 对于函数模板的使用, 编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用.
例如: 当用 double 类型使用函数模板时, 编译器通过对实参类型的推演, 将 T 确定为 double 类型, 然后产生一份专门处理 double 类型的代码
注意 函数模板的模板参数类型可以写 typename, 也可以写 class (不可以写 struct).
函数模板的实例化
用不同类型的参数使用函数模板时, 称为函数模板的实例化.

隐式实例化: 让编译器根据实参推演模板参数的实际类型
template <typename T>
T Add(const T &left, const T &right)
{return left + right;
}int main()
{int a1 = 10, b1 = 20;Add(a1, b1); // 推演出模板参数类型为 intdouble a2 = 1.1, b2 = 2.2;Add(a2, b2); // 推演出模板参数类型为 doubleAdd(a1, b2); // 推演失败return 0;
}
前两个可以通过编译, 函数模板类型相同, 编译器可以推演出函数模板的类型.
第三个传入了一个 int 类型和一个 double 类型, 编译器推演模板参数类型失败.

因为定义模板函数的时候, 规定了模板函数参数只有一个类型 T, 传入两个类型, 编译器不能确定应该是将 T 演绎成哪个类型.
在模板中, 编译器不会进行类型转换操作, 一旦转换出问题, 编译器就会背黑锅, 所以不会隐式转换.
有两种处理方式:
- 用户自己进行强化类型转换
- 使用显示实例化
Add(a1, (int)b2); // 用户自己强制类型转换
显示实例化: 在函数名后的<>中制定模板参数的世纪类型
int main()
{int a1 = 10;double b = 20.0;Add<int> (a1, b2); // 显示实例化return 0;
}
如果类型不匹配, 编译器会进行隐式类型转换, 如果无法转换成功, 编译器会报错.
如果函数参数没有模板参数, 那么就无法使用隐式实例化, 只能进行显示实例化.
// 申请一个T类型十个元素的数组并返回
template <typename T>
T *f()
{T *p = new T[10];return p;
}int main()
{int *p1 = f<int>();double *p2 = f<double>();return 0;
}
模板参数的匹配原则
- 一个非模板函数和一个同名的函数模板同时存在, 而且该函数模板可以被实例化为这个非模板函数
// 专门处理 int 类型的加法函数
int Add(const int &left, const int &right)
{cout << "(int) Add" << endl;return left + right;
}// 通用加法函数
template <typename T>
T Add(const T &left, const T &right)
{cout << "(template) Add" << endl;return left + right;
}int main()
{Add(1, 2); // 有现成的优先用现成的Add<int>(1, 2); // 如果指定显示实例化, 用函数模板
}

- 对于非模板函数和同名函数, 如果其他条件都相同, 在调用时会优先调用非模板函数而不会从该模板产生一个实例. 如果模板可以产生一个具有更好匹配的函数, 那么将选择模板.
// 专门处理 int 类型的加法函数
int Add(const int &left, const int &right)
{cout << "(int) Add" << endl;return left + right;
}// 通用加法函数
template <typename T1, typename T2>
T1 Add(const T1 &left, const T2 &right)
{cout << "(template) Add" << endl;return left + right;
}int main()
{Add(1, 2); // 与非函数模板完全匹配, 不需要函数模板实例化Add(1, 2.0); // 有更合适的不会进行隐式类型转换调用非函数模板, 而会直接调用函数模板实例化的实例
}

- 函数模板不允许自动类型转换, 而普通函数可以进行自动类型转换
// 专门处理 int 类型的加法函数
int Add(const int &left, const int &right)
{cout << "(int) Add" << endl;return left + right;
}// 通用加法函数
template <typename T>
T Add(const T &left, const T &right)
{cout << "(template) Add" << endl;return left + right;
}int main()
{Add(1, 2.0); // 函数模板不能进行自动类型转换, 只能调用普通函数, 传参的时候进行自动类型转化
}

总结
- 优先调用现有的普通函数
- 没有函数模板, 普通函数参数可以自动类型转换的, 使用普通函数.
- 可以通过函数模板实例化更合适的函数, 哪怕普通函数可以自动类型转换, 也用函数模板.
三. 类模板
以前写数据结构的时候, 通常会用 typedef 重命名数据结构内数据的类型.
例如 Stack 中用 typedef int STDataType, 制定 Stack 中的数据类型是 int 类型的.
但是, 这样只能保证一次只能用 int, 如果需要用到 double 的, 需要再重新写一份.
有了类模板就能很好的解决这个问题.
类模板的定义格式
template <class T1, class T2,...,class T3>
class 类模板名
{// 类内成员定义
};
例如: Stack
template <class T>
class Stack
{
public:Stack(size_t capacity = 10): _array(new T[capacity]), _capacity(capacity), _top(0){}~Stack();void push(const T &data);//...
private:T *_array;int _capacity;int _top;
};// 类外定义成员函数必须要加模板参数列表
template <class T>
Stack<T>::~Stack()
{delete[](_array);_capacity = _top = 0;
}
类外定义成员函数, 必须要加上模板参数列表声明, 并且指定类域时必须加上模板参数列表.
同时, 类模板成员函数的声明与定义必须放在同一个文件中, 否则编译器会链接失败.
类模板的实例化
类模板实例化与函数模板实例化不同, 类模板都是显示实例化, 需要在类模板名字后面跟<>, 然后将实例化的类型放在<>中即可, 类模板名字不是真正的类, 而实例化的结果才是真正的类
Stack<int> s1;
Stack<double> s2;
普通类的类名即是类型, 而类模板的类名不是类型, 类名<数据类型> 才是整个类的类型.
Stack<int> 和 Stack<double> 不是同一个类, 他们只是同一类模板显示实例化生成的不同类.
本章完.
相关文章:
C++: 模板初阶
文章目录 一. 泛型编程二. 函数模板函数模板的原理函数模板的实例化隐式实例化: 让编译器根据实参推演模板参数的实际类型显示实例化: 在函数名后的<>中制定模板参数的世纪类型 模板参数的匹配原则 三. 类模板类模板的定义格式类模板的实例化 一. 泛型编程 如何实现一个…...
人工智能基础_机器学习036_多项式回归升维实战3_使用线性回归模型_对天猫双十一销量数据进行预测_拟合---人工智能工作笔记0076
首先我们拿到双十一从2009年到2018年的数据 可以看到上面是代码,我们自己去写一下 首先导包,和准备数据 from sklearn.linear_model import SGDRegressor import numpy as np import matplotlib.pyplot as plt X=np.arange(2009.2020)#左闭右开,2009到2019 获取从2009到202…...
【算法挨揍日记】day29——139. 单词拆分、467. 环绕字符串中唯一的子字符串
139. 单词拆分 139. 单词拆分 题目描述: 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 解题思路&am…...
YOLOv8-Seg改进:轻量级Backbone改进 | VanillaNet极简神经网络模型 | 华为诺亚2023
🚀🚀🚀本文改进:一种极简的神经网络模型 VanillaNet,支持vanillanet_5, vanillanet_6, vanillanet_7, vanillanet_8, vanillanet_9, vanillanet_10, vanillanet_11等版本,相比较yolov8-seg各个版本如下: layersparametersgradientsGFLOPsvanillanet_521230017523...
解决Requests中使用httpbin服务器问题:自定义URL的实现与验证
问题背景 在使用Python的Requests模块进行单元测试时,可能会遇到无法使用本地运行的httpbin服务器进行测试的问题。这是因为测试脚本允许通过环境变量HTTPBIN_URL指定用于测试的本地httpbin实例,但在某些测试用例中,URL是硬编码为httpbin.or…...
软考-高级-系统架构设计师教程(清华第2版)【第17章 通信系统架构设计理论与实践(P614~646)-思维导图】
软考-高级-系统架构设计师教程(清华第2版)【第17章 通信系统架构设计理论与实践(P614~646)-思维导图】 课本里章节里所有蓝色字体的思维导图...
【MATLAB源码-第82期】基于matlab的OFDM系统载波频移偏差(CFO)估计,对比三种不同的方法。
操作环境: MATLAB 2013b 1、算法描述 正交频分复用(OFDM)系统中的载波频率偏移(CFO)估计是一项关键技术,用于确保数据传输的准确性和效率。CFO通常由于振荡器频率不匹配和多普勒频移引起。不同的CFO估计…...
Docker Swarm: 容器编排的力量和优势深度解析
文章目录 Docker Swarm的核心概念1. 节点(Node)2. 服务(Service)3. 栈(Stack) 使用Docker Swarm1. 初始化Swarm2. 加入节点3. 创建服务4. 扩展和缩减服务5. 管理栈6. 管理服务更新 Docker Swarm的优势深度解…...
调整Windows键盘上只能看到拼音而无法看到实际的文本以及关闭输入法悬浮窗方法
一、输入法设置 如果您在键盘上只能看到拼音而无法看到实际的文本,这可能是因为您的输入法设置为中文拼音输入法或其他仅显示拼音的输入法。 要解决这个问题,您可以尝试以下方法: 1. 切换输入法:按下 Shift Alt 组合键或 Wind…...
【微软技术栈】C#.NET 中的管道操作
C#.NET 管道为进程间通信提供了平台。 管道分为两种类型: 匿名管道。 匿名管道在本地计算机上提供进程间通信。 与命名管道相比,虽然匿名管道需要的开销更少,但提供的服务有限。 匿名管道是单向的,不能通过网络使用。 仅支持一个服…...
Python学习笔记--进程
进程 Python 中的多线程其实并不是真正的多线程,如果想要充分地使用多核 CPU 的资源,在 Python 中大部分情况需要使用多进程。 Python 提供了非常好用的多进程包 multiprocessing,只需要定义一个函数,Python 会完成其他所有事情。 借助这个包,可以轻松完成从单进程到并…...
比亚迪刀片电池与特斯拉4680电池比较
1 电池材料 比亚迪刀片电池采用的磷酸铁锂LFP(LiFePO4),特斯拉的4680电池采用的三元锂。 磷酸铁锂:循环寿命长,安全性能好,价格低廉,但是能量密度低,导电性能差,低温表现…...
在写windows C++代码的时候,从代码安全角度考虑,我们应该注意什么?
在写windows C代码的时候,从代码安全角度考虑,我们应该注意什么?分别是:输入验证、内存管理、错误处理、并发和线程安全、使用安全的API、避免使用不安全的函数、最小权限原则。 一、输入验证 1. 用户输入验证 #include <io…...
【草料】uni-app ts vue 小程序 如何如何通过草料生成对应的模块化二维码
一、查看uni-app项目 1、找到路径 可以看到项目从 src-race-pages-group 这个使我们目标的查询页面 下面我们将这个路径copy到草料内 2、找到进入页面入参 一般我们都会选择 onload() 函数下的入参 这里我们参数的是 id 二、草料 建议看完这里的教程文档 十分清晰!…...
CMS与FullGC
JVM中的CMS(Concurrent Mark Sweep)GC和Full GC(Full Garbage Collection)是两种不同的垃圾回收算法。 CMS GC:CMS GC是一种并发的垃圾回收算法,它在运行期间与应用程序线程并发工作,尽可能减少…...
一款.NET开源的小巧、智能、免费的Windows内存清理工具 - WinMemoryCleaner
前言 我们在使用Windows系统的时候经常会遇到一些程序不会释放已分配的内存,从而导致电脑变得缓慢。今天给大家推荐一款.NET开源的小巧、智能、免费的Windows内存清理工具:WinMemoryCleaner。 使用Windows内存清理工具来优化内存,这样不必浪…...
iptables详解:链、表、表链关系、规则的基本使用
目录 防火墙基本概念 什么是防火墙? Netfilter与iptables的关系 链的概念 表的概念 表链关系 规则的概念 查询规则 添加规则 删除iptables中的记录 修改规则 更详细的命令(5链4表) 防火墙基本概念 什么是防火墙? 在…...
安全管理中心(设备和技术注解)
网络安全等级保护相关标准参考《GB/T 22239-2019 网络安全等级保护基本要求》和《GB/T 28448-2019 网络安全等级保护测评要求》 密码应用安全性相关标准参考《GB/T 39786-2021 信息系统密码应用基本要求》和《GM/T 0115-2021 信息系统密码应用测评要求》 1系统管理 1.1对系统管…...
Failed to execute org.scala-tools:maven-scala-plugin:2.15.2解决
原因也不是很清楚,查看一个博主文章(net.alchim31.maven:scala-maven-plugin:maven依赖无法下载或无法编译)得到的解决方案: 在idea的terminal执行以下语句即可实现maven对scala代码的编译: mvn clean scala:compile compile pac…...
C#中委托和事件的使用总结
委托(delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。事件是一种特殊的多播委托,仅可以从声明事件的类或结构中对其进行调用。类或对象可以通过事件向其他类或对象通知发生的…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
