CPP初级:模板的运用!
目录
一.泛型编程
二.函数模板
1.函数模板概念
2.函数模板格式
3.函数模板的原理
三.函数模板的实例化
1.隐式实例化
2.显式实例化
3.模板参数的匹配原则
四.类模板
1.类模板的定义格式
2.类模板的实例化
一.泛型编程
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。
泛型编程是一种编程范式,它允许程序员编写不依赖于特定数据类型的代码。
在泛型编程中,程序员可以定义一些通用的算法和数据结构,这些可以在不同的数据类型中使用。
比如交换函数,如果我们没有学习泛型编程,则我们就需要根据类型的交换,造出多个轮子:
typedef int Type;
void Swap(Type& left, Type& right)
{Type temp = left;left = right;right = temp;}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;
}
使用函数重载固然可以实现这一问题,但是有几个不好的地方:
- 代码空间会变大
- 重载的函数只是类型不同,代码的复用率较低
- 只要有新的类型需要使用这个函数,就需要重载新的函数
- 代码的可维护性较低,一个出错可能全部的重载都出错
因为函数重载存在上述缺点,因此我们提出了”函数模板“
在现实生活中,我们可以通过往模具中填充不同的材料生成不同的铸件。
C++的开发者受到了启发,发明了模板。
模板:告诉编译器一个模子,让编译器根据不同的类型利用该模子生成代码。
模板可以分为函数模板和类模板:
模板是泛型编程的基础。
二.函数模板
1.函数模板概念
函数模板代表了一个函数家族,该家族模板与类型无关。
函数模板在使用时被参数化,根据实参类型产生的特定类型版本。
2.函数模板格式
我们用templata关键字来声明模板:
template <typename T1, typename T2,......typename Tn>
返回值类型 函数名(参数列表)
{//函数体
}
这里需要大家注意的是,我们的第一行后面并没有分号,也就代表着它并不是一条语句。
现在我们举出一个实例:
template <typename T>//函数模板的声明
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}
此外,我们也可以使用class取代typename
template <class T>//函数模板的声明
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}
现在我们使用一下我们定义的函数模板
#include <iostream>
using namespace std;
template <class T>//函数模板的声明
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}
int main()
{int a = 1, b = 2;Swap(a, b);cout << a << ' ' << b << endl;double c = 1.3, d = 2.5;Swap(c, d);cout << c << ' ' <<d << endl;return 0;
}
可以看到,这里圆满的完成了交换逻辑。
3.函数模板的原理
那么,上述问题是如何解决的呢?
大家都知道,瓦特改良蒸汽机,人类开始了工业革命,解放了生产力。
机器生产淘汰掉了很多手工产品。
本质是什么,重复的工作交给了机器去完成。
因此有人给出了论调:懒人创造世界。
函数模板的本质也是如此
现在我们进入汇编来看一下上述代码运行的过程中编译器都干了什么事
可以看到,这里调用函数时,显式的规定了参数的类型。
因此我们可以得到结论:函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。
我们的编译器根据这个模具帮我们做了这个事情。
注意:Swap在调用时,调用的不是void Swap(T& left, T& right),而是编译器预先根据要调用的类型进行推演。
编译器负责在编译时分析模板定义,并在需要时生成特定类型的代码,之后编译器会检查模板的语法,并确保模板的使用是合法的,之后编译器会根据实际使用的类型参数生成相应的函数或类的实现。
例如上图中的这两行代码:
00007FF6E7122423 call Swap<int> (07FF6E7121352h)
00007FF6E7122480 call Swap<double> (07FF6E7121398h)
这两个函数模板就是编译器生成的。
在编译器的编译阶段,编译器就会根据传入的实参类型来推演生成对应类型的函数以供调用。
就比如上图: 当用double
类型使用函数模板时,编译器通过对实参类型的推演,将T
确定为double
类型,然后产生一份专门处理double
类型的代码。
三.函数模板的实例化
用不同类型的参数使用函数模板称为函数模板的实例化。
模板参数的实例化可以分为:隐式实例化和显式实例化。
1.隐式实例化
隐式实例化即我们刚刚实例化的方法,这里不再过多赘述。
#include <iostream>
using namespace std;
template <class T>//函数模板的声明
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;Add(a1, a2);cout << Add(a1,a2) << endl;return 0;
}
现在我们来看一下这段代码:
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.1, d2 = 20.2;Add(a1, d2);cout << Add(a1, d2) << endl;return 0;
}
这段代码在大部分编译器下是无法运行的,在VS2022中爆出了如下警告:
为什么在大部分编译器下无法通过编译呢?
这是因为在编译期间,当编译器看到该实例化后,会去推演其实参的类型。
但通过实参a1将T推演为了int,通过实参d1将T推演为double类型。
但是模板参数列表中只有一个T,编译器就无法判断T在这里是int还是double。
为什么在vs2022中可以编译成功呢?
这是因为编译器进行了类型转换操作,但是类型转换操作的风险是极大的,因为不知道此处你想要的是double还是int。
那么我们应该处理这个问题呢?
处理方式1:用户自己强制转换
Add(a1, (int)d2);//想要int类型的,我们直接将d2强转为int
处理方式2:采用显式实例化
那么,如何显式实例化呢?
2.显式实例化
显式实例化:在函数名的后面,参数列表的前面加一对尖括号<>,尖括号内部指定模板参数的实际类型。如下:
Add<int>(a, b);
如果参数类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
3.模板参数的匹配原则
- 在我们的程序中,一个非模板函数是可以和一个同名的函数模板同时存在的,而且该函数模板还可以被实例化为这个非模板函数
T Add(const T& left, const T& right)
{cout << "T Add(T& left,T& right)" << endl;return left + right;
}
int Add(const int& left, const int& right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
int main()
{Add(1, 2);Add<int>(1, 2);return 0;
}
我们运行之后,可以看到如下的结果:
我们发现,第一个Add函数调用了专门处理int类型的加法函数,而第二个Add函数调用了模板。
那么,为什么第一个Add函数不调用函数模板呢?
这是因为如下内容:
- 对于非模板函数和同名函数模板,如果其他条件都相同,在调用时会优先调用非模板函数而不会从该模板产生出一个实例。
- 如果条件不同的话,则会选择模板。
- 也就是说,编译器会优先调用更加匹配的版本调用!
我们可以看一下下面的这段代码:
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
// 通用加法函数
template<class T1, class T2>//注意这里有两个类型
T1 Add(T1 left, T2 right)
{cout << "T1 Add(T1 left, T2 right)" << endl;return left + right;
}
int main()
{Add(1, 2);// 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2.0);// 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数return 0;
}
可以看到,第一个调用了非模板函数,第二个调用了模板函数。
- 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
对于模版T1 Add(T1 left, T2 right)
不知道返回值是T1或T2,可以选择auto,auto虽然不太适合做返回值,但是对于简单普通函数操作,可以进行自动类型转换。
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}//auto可作简单处理的函数返回值
template<class T1, class T2>
auto Add(const T1& left, const T2& right)
{cout << "auto Add(const T1& left, const T2& right)" << endl;return left + right;
}
int main()
{Add(1, 2);// 与非函数模板类型完全匹配,不需要函数模板实例化cout << Add(1, 2) << endl;Add(1, 2.0);// 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数cout << Add(1, 2.0) << endl;return 0;
}
四.类模板
1.类模板的定义格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};
// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}~Vector();//类外定义void PushBack(const T& data);void PopBack();// ...size_t Size() { return _size; }T& operator[](size_t pos){assert(pos < _size);return _pData[pos];}
private:T* _pData;size_t _size;size_t _capacity;
};
// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{if (_pData)delete[] _pData;_size = _capacity = 0;
}
模版Vector中只是提供了一个模具,具体印刷出什么模型,是由编译器最终实例化决定的。
注意:模版不建议声明和定义分离到.h
和.cpp,
会出现链接错误,要分离也分离在.h。
2.类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字和变量名字中间加一个尖括号<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
Vector<int> intvector;
Vector<string> stringvector;
码字不易,如果你觉得博主写的不错的话,可以关注一下博主哦。
相关文章:

CPP初级:模板的运用!
目录 一.泛型编程 二.函数模板 1.函数模板概念 2.函数模板格式 3.函数模板的原理 三.函数模板的实例化 1.隐式实例化 2.显式实例化 3.模板参数的匹配原则 四.类模板 1.类模板的定义格式 2.类模板的实例化 一.泛型编程 泛型编程:编写与类型无关的通用代码…...
排序---基数排序
前言 个人小记 一、简介 基数排序是一种非比较排序,所以排序速度较快,当为32位int整数排序时,可以将数分为个位十位分别为2^16,使得拷贝只需要两轮,从而达到2*n,然后给一个偏移量,使得可以对负数排序。以…...

“新高考”下分班怎么分?
来自安徽的张女士告诉我:上一年孩子升入了高中,但没想到才高一,孩子就面临了一个困难的挑选:312”分班! 什么是312”分班呢?许多人或许不明白,便是要求学生在高一入学时,针对于3门必…...
二叉树的层序遍历-力扣
本题是二叉树的层序遍历,通过一个队列来控制遍历的节点,二叉树每层的节点和上一层入队的节点个数是相同的,根据这一点编写循环条件。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* …...

N32G45XVL-STB之移植LVGL(lvgl-8.2.0)
目录 概述 1 软硬件介绍 1.1 软件版本信息 1.2 ST7796-LCD 1.3 MCU IO与LCD PIN对应关系 2 认识LVGL 2.1 LVGL官网 2.2 LVGL库文件下载 3 移植LVGL 3.1 准备移植文件 3.2 添加lvgl库文件到项目 3.2.1 src下的文件 3.2.2 examples下的文件 3.2.3 配置文件路径 3.2…...
【设计模式】创建型设计模式之 原型模式
介绍 原型模式是一种创建型设计模式,主要用于创建重复的对象,而无需重新初始化它们,从而提高效率并简化对象的创建过程。此模式的核心思想是利用已存在的对象实例,通过复制(克隆)的方式来生成新的对象&…...

【类型商店】字符字符串(下)
啊,哈喽,小伙伴们大家好。我是#Y清墨,今天呐,我要介绍的是字符与字符串。 导语 前两期,我们已经懂得了概念,今天来看些函数。 正题 一.增加或连接 (1) 后面增加() string s1,s2; //定义 s…...

『 Linux 』内存管理与文件系统
文章目录 交换分区页与页框(页帧)交换分区与内存之间的交换操作系统如何管理内存物理地址转换页号与页内偏移量 内存管理,文件系统与文件管理之间的联系 交换分区 在Linux的安装过程中,用户将会被提示创建一个交换分区; 这是一个特殊的分区,其大小可以由用户根据系统内存需求和…...

线性代数|机器学习-P8矩阵低秩近似eckart-young
文章目录 1. SVD奇异值分解2. Eckart-Young2.1 范数 3. Q A Q U Σ V T QAQU\Sigma V^T QAQUΣVT4. 主成分分析图像表示 1. SVD奇异值分解 我们知道,对于任意矩阵A来说,我们可以将其通过SVD奇异值分解得到 A U Σ V T AU\Sigma V^T AUΣVT࿰…...

平面设计神器CorelDRAW2021精简版,你值得拥有!
亲爱的设计师小伙伴们,今天我要为大家种草一款神奇的软件——CorelDRAW平面设计软件2021精简版!🤩✨作为一名专业的图形设计师,我深知一个好工具对于我们的工作有多么重要。而这款软件简直就是我们设计师的救星!&#…...
kafka是什么?
Kafka是一个由Apache软件基金会开发的开源流处理平台,最初由LinkedIn公司开发,使用Scala和Java编写。它是一个高吞吐量的分布式发布订阅消息系统,可以处理消费者在网站中的所有动作流数据,如网页浏览、搜索和其他用户行为等。Kafk…...
ABC351
C 栈的应用 #include<bits/stdc.h>using namespace std;stack<int>stk;int main() {int n;cin>>n;for(int i1;i<n;i){int a;cin>>a;while(!stk.empty()&&astk.top()){stk.pop();a;}stk.push(a);}cout<<stk.size()<<endl;retur…...
base上海,数据科学,数据挖掘,数据分析等岗位求收留
裁员了,base上海,数据科学,数据挖掘,数据分析等岗位,期望30k~40k,求推荐求收留 1,6年数据算法工作,做过指标体系搭建,用户画像,货品定价,社区分析…...

IC元器件
1.电阻: 电阻的作用: 1.与负载串联:做限流分压 2.电阻并联:将小功率电阻并联成大功率,防烧毁 2.电容: 电容就是两块金属板+中间的介质(相当于两个人坐在一起加上中间的空气…...

SQL159 每个创作者每月的涨粉率及截止当前的总粉丝量
描述 用户-视频互动表tb_user_video_log iduidvideo_idstart_timeend_timeif_followif_likeif_retweetcomment_id110120012021-09-01 10:00:002021-09-01 10:00:20011NULL210520022021-09-10 11:00:002021-09-10 11:00:30101NULL310120012021-10-01 10:00:002021-10-01 10:00…...

Linux安装MySQL教程【带图文命令巨详细】
巨详细Linux安装MySQL 1、查看是否有自带数据库或残留数据库信息1.1检查残留mysql1.2检查并删除残留mysql依赖1.3检查是否自带mariadb库 2、下载所需MySQL版本,上传至系统指定位置2.1创建目录2.2下载MySQL压缩包 3、安装MySQL3.1创建目录3.2解压mysql压缩包3.3安装解…...

外部排序快速入门详解:基本原理,败者树,置换-选择排序,最佳归并树
文章目录 外部排序1.最基本的外部排序原理2.外部排序的优化2.1 败者树优化方法2.2 置换-选择排序优化方法2.3 最佳归并树 外部排序 为什么要学习外部排序? 答: 在处理数据的过程中,我们需要把磁盘(外存)中存储的数据拿到内存中处理…...

人工智能和物联网如何结合
欢迎来到 Papicatch的博客 目录 🍉引言 🍉AI与IoT的结合方式 🍈数据处理和分析 🍍实例 🍈边缘计算 🍍实例 🍈自动化和自主操作 🍍实例 🍈安全和隐私保护 &…...

【JAVASE】JAVA应用案例(下)
一:抢红包 一个大V直播时,发起了抢红包活动,分别有9,666,188,520,99999五个红包。请模拟粉丝来抽奖,按照先来先得,随机抽取,抽完即止,注意:一个红包只能被抽一次,先抽或…...

【面试干货】 B 树与 B+ 树的区别
【面试干货】 B 树与 B 树的区别 1、B 树2、 B 树3、 区别与优缺点比较4、 总结 💖The Begin💖点点关注,收藏不迷路💖 在数据库系统中,B 树和 B 树是常见的索引结构,它们在存储和组织数据方面有着不同的设计…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...