C++模板详解(函数模板、类模板)

2023-11-18

hello,这里是bangbang,今天来讲下模板

目录

1. 泛型编程

2. 函数模板

  2.1 函数模板概念

  2.2 函数模板格式

  2.3 函数模板的实例化

  2.4 模板参数的匹配原则

3. 类模板

  3.1 类模板定义格式

  3.2 类模板实例化

4. 非类型模板参数

5. 模板特化

  5.1 模板特化概念

  5.2 函数模板特化

  5.3 类模板特化

6. 模板分离编译

  6.1 什么是分离编译

  6.2 模板的分离编译

7. 总结


1. 泛型编程

如何实现一个通用的交换函数?

有的小伙伴或许会想到用函数重载,如下:

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;
}

但是这就出现问题了,只要我类型不符合函数参数,那我就要再重载一份,这也太麻烦了!

1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
2. 代码的可维护性比较低,一个出错可能所有的重载均出错

那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢? 

泛型编程: 编写与类型无关的通用代码,是代码复用的一种手段。 模板是泛型编程的基础

2. 函数模板

  2.1 函数模板概念

        函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
        函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。

  2.2 函数模板格式

template<typename T1, typename T2,......,typename Tn>
返回值 函数名(参数列表)
{}
注意:typename是用来定义模板参数关键字也可以使用class(切记:不能使用struct代替class)

交换函数的函数模板(2项参数类型相同):

template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

        在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码

  2.3 函数模板的实例化

        用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为: 隐式实例化 显式实例化

        1.隐式实例化:让编译器根据实参推演模板参数的实际类型

函数模板(假设1个模板参数),如果出现类型不匹配,会发生什么呢?

我们都知道C++内置类型进行运算,会向上算术转换,比如int+double=double,那么函数模板在这块是如何处理的呢,也可以直接将结构转换成double?我们来看:

此时有两种处理方式:1.用户自己强制转换。2.使用显示实例化。

用户自己强制转换:

Add(a1,(int)d1);

        2.显示实例化:在函数名后的<>中指定模板参数的实际类型

Add<int>(a1,d1);//显示实例化

  2.4 模板参数的匹配原则

  • 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
在VS下,打断点按F11进行查看,发现只有Add<int>()进入函数模板
  • 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

  • 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

3. 类模板

  3.1 类模板定义格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{
 // 类内成员定义
};

 注意:类模板中函数放在类外进行定义时,需要加模板参数列表

//类模板
template<class T>
class Vector
{}

//类外定义
template<class T>
Vector<T>::函数定义

  3.2 类模板实例化

类模板实例化与函数模板实例化不同, 类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可 ,类模板名字不是真正的类,而 实例化的结果才是真正的类
//Vector类名,Vector<int>类型
Vector<int> v;

4. 非类型模板参数

模板参数分类: 类型形参 非类型形参
类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
namespace lh
{
	// 定义一个模板类型的静态数组
	template<class T, size_t N = 10>
	class array
	{
	public:
		T& operator[](size_t index) { return _array[index]; }
		const T& operator[](size_t index)const { return _array[index]; }

		size_t size()const { return _size; }
		bool empty()const { return 0 == _size; }

	private:
		T _array[N];
		size_t _size;
	};
}
注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果。

在这里补充一下array,我们想想平时写代码,数组用的是C++的array,还是C的数组形式。为什么C++要整出一个array类呢?

 既然大佬整了出来,那肯定是和C有一定区别的!不知道小伙伴平时有没有发现C语言的数组形式对越界检查是非常不严格的!

 

 优势:

 C语言中越界检查采取抽查方式指针解引用),越界读不报错,越界写有限报错。

 C++11,array只要越界一定能查到(函数调用)。


5. 模板特化

  5.1 模板特化概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
     return left < right;
}
int main()
{
     cout << Less(1, 2) << endl; // 可以比较,结果正确
     Date d1(2022, 7, 7);
     Date d2(2022, 7, 8);
     cout << Less(d1, d2) << endl; // 可以比较,结果正确
     Date* p1 = &d1;
     Date* p2 = &d2;
     cout << Less(p1, p2) << endl; // 可以比较,结果错误
     return 0;
}
可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指针的地址,这就无法达到预期而错误。
此时,就需要对模板进行特化。即: 在原模板类的基础上,针对特殊类型所进行特殊化的实现方式 。模板特 化中分为 函数模板特化 类模板特化

  5.2 函数模板特化

函数模板的特化步骤:
  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
	return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}
void date_test()
{
	cout << Less(1, 2) << endl;
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl;
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
}
注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是 将该函数直接给出
bool Less(Date* left, Date* right)
{
     return *left < *right;
}
该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此 函数模板不建议特化

  5.3 类模板特化

全特化:将模板参数列表中所有的参数都确定化。
偏特化:部分特化(针对指针类型/针对引用类型/指针、引用复合类型)

typename还有告诉编译器这是类型还是变量的作用!

因为静态类型变量指定类域也可以访问,所以在使用类型时,我们前面加typename进行区分。(不加编译器区分不清楚是类型还是变量)


6. 模板分离编译

  6.1 什么是分离编译

        一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
        模板不支持分离编译!

  6.2 模板的分离编译

 链接问题:都是有声明找不到定义

7. 总结

优点:

        1.模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。(重复的工作交给编译器做)

        2.增强了代码的灵活性。

缺点:

        1.模板会导致代码膨胀问题,也会导致编译时间变长。

        2.出现模板编译错误时,错误信息非常凌乱,不易定位错误。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++模板详解(函数模板、类模板) 的相关文章

  • 如何获取正在访问 ASP.NET 应用程序的当前用户?

    为了获取系统中当前登录的用户 我使用以下代码 string opl System Security Principal WindowsIdentity GetCurrent Name ToString 我正在开发一个 ASP NET 应用程
  • 编译时运算符

    有人可以列出 C 中可用的所有编译时运算符吗 C 中有两个运算符 无论操作数如何 它们的结果始终可以在编译时确定 它们是sizeof 1 and 2 当然 其他运算符的许多特殊用途可以在编译时解决 例如标准中列出的那些整数常量表达式 1 与
  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • 为什么两个不同的 Base64 字符串的转换会返回相等的字节数组?

    我想知道为什么从 base64 字符串转换会为不同的字符串返回相同的字节数组 const string s1 dg const string s2 dq byte a1 Convert FromBase64String s1 byte a2
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • 嵌套接口:将 IDictionary> 转换为 IDictionary>?

    我认为投射一个相当简单IDictionary
  • 如何使用 ICU 解析汉字数字字符?

    我正在编写一个使用 ICU 来解析由汉字数字字符组成的 Unicode 字符串的函数 并希望返回该字符串的整数值 五 gt 5 三十一 gt 31 五千九百七十二 gt 5972 我将区域设置设置为 Locale getJapan 并使用
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • C#中如何移动PictureBox?

    我已经使用此代码来移动图片框pictureBox MouseMove event pictureBox Location new System Drawing Point e Location 但是当我尝试执行时 图片框闪烁并且无法识别确切
  • C++ OpenSSL 导出私钥

    到目前为止 我成功地使用了 SSL 但遇到了令人困惑的障碍 我生成了 RSA 密钥对 之前使用 PEM write bio RSAPrivateKey 来导出它们 然而 手册页声称该格式已经过时 实际上它看起来与通常的 PEM 格式不同 相
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • 为什么编译时浮点计算可能不会得到与运行时计算相同的结果?

    In the speaker mentioned Compile time floating point calculations might not have the same results as runtime calculation
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 基于 OpenCV 边缘的物体检测 C++

    我有一个应用程序 我必须检测场景中某些项目的存在 这些项目可以旋转并稍微缩放 更大或更小 我尝试过使用关键点检测器 但它们不够快且不够准确 因此 我决定首先使用 Canny 或更快的边缘检测算法 检测模板和搜索区域中的边缘 然后匹配边缘以查
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co

随机推荐

  • AIOps 在美团的探索与实践 —— 故障发现篇

    摘要 AIOps 最初的定义是Algorithm IT Operations 是利用运维算法来实现运维的自动化 最终走向无人化运维 随着技术成熟 逐步确定为Artificial Intelligence for IT Operations
  • Java ee学习笔记

    Servlet简介 Servlet技术规范是JavaEE技术规范中的一个重要组成部分 Servlet是一种独立于平台和协议的服务器端的Java应用程序 可以生成动态的Web页面 实际上 Servlet不仅仅是用于返回HTML的页面的 比如
  • 创建简单的Windows驱动程序并与应用程序通信

    创建简单的Windows驱动程序并与应用程序通信 在本篇博客中 我们将创建一个简单的Windows驱动程序 并实现与用户模式应用程序的通信 我们将展示如何创建驱动程序 处理IRP请求 并在应用程序中使用CreateFile和WriteFil
  • MXNet==1.9.x 源代码编译流程(windows + python)

    只为mxnet for win开发做做小贡献 mxnet深度玩家 写在开头 注意windows powershell 第三方依赖 python git 7zip cmake ninja opencv等 的安装和运行版本 x64和x84 须统
  • 【算法】稳定匹配(C++版)

    由于学习需要 然后花费将近两天时间研究这个问题 然后用C 描述出来 具体内容看下面 问题描述 见百度百科 https baike baidu com item E7 A8 B3 E5 AE 9A E5 A9 9A E5 A7 BB E9 9
  • Qt - 使用子目录项目来 配置多个子工程/子模块

    QT 使用子目录项目来 配置多个子工程 子模块 简述 项目配置概览 项目的多工程配置 使用多模块 特别鸣谢 Qt 之 pro 配置多个子工程 子模块 Qt Creator创建子目录项目并自定义目标文件输出目录 使用子目录项目来 配置多个子工
  • unity打包后无法读取Excel解决方法

    一 前言 最近几乎遇到了所有能遇到的unity读取Excel 的问题 因为使用的是unity5 4 而且还是32位 所以出现各种问题在所难免 废话不多说 现有的现象是 在unity的编辑器里可以完美运行 读取Excel不成问题 但是打包成e
  • 朋友们,想去一线大厂?卷起来...

    大家好 我是Tom哥 计算机研究生 校招进阿里 P7技术专家 出过专利 CSDN博客专家 负责过电商交易 社区团购 流量营销 金融等业务 多年一线团队管理经验 多年的大厂浸染 参加多次淘宝双11大促活动 在系统架构方面有丰富经验 为了帮助大
  • vue如何阻止事件冒泡

    vue阻止事件冒泡 div span bt1 span span bt2 span div click stop这样点击bt1就不会执行fatherFunc
  • sql注入_字符型、数字型判断

    如何判断sql注入是字符型还是数字型 在进行sql注入时 字段类型分为字符型或者数字型 意味着我们需要构造不同的sql语句 假设存在sql注入的url是 http 192 168 0 1 id 1 数字型判定 1 and或者or 假设数据库
  • 基于springboot+vue高校实验室教学管理系统【附源码】

    晚安独角兽 hello你好我是独角兽 很高兴你能来阅读 昵称是希望自己能不断精进 向着优秀程序员前行 博客来源于项目以及编程中遇到的问题总结 偶尔会有读书分享 我会陆续更新Java前端 后台 数据库 项目案例等相关知识点总结 感谢你的阅读和
  • 数据工程师、商业智能( BI )工程师和机器学习( ML )工程师都具体做什么?

    如果你在考虑从事数据科学的工作 可能会觉得这个领域有点令人困惑 什么是数据科学家 数据分析师和数据科学家之间有什么区别 机器学习工程师做什么 那么数据工程师 商业智能 BI 工程师和机器学习 ML 研究员呢 在这篇文章中 我们将描述数据科学
  • gitlab访问报错: Whoops, GitLab is taking too much time to respond

    目录 问题描述 解决方法 问题原因 问题描述 同学们玩gitlab的时候 通过网页访问报错 Whoops GitLab is taking too much time to respond 解决方法 等着就好了 问题原因 我们来分析一下原因
  • element-ui+vue-router:实现导航栏跳转路由

    在实际开发中我们常常遇到在单页面中点击导航栏菜单中的某一选项卡 页面中的某个部分出现相关的信息 也就是使用导航栏进行路由跳转 如下图所示 在线格式转换 示例
  • cuda11.3版本怎么安装pytorch1.5

    首先 确保你的电脑已经安装了CUDA 11 3和对应版本的驱动程序 然后 可以使用pip命令安装PyTorch 1 5 具体方法如下 pip install torch 1 5 0 cu113 torchvision 0 6 0 cu113
  • 《阿甘正传》英文原版剧本(场景+台词)(看电影学英语的最佳材料)

    当前在我朝可以下载到的 阿甘正传 的视频对应的所有版本的字幕 貌似都是字幕制作者听出来的结果 客气地说 这些版本的字幕都是不 完整 的 或者说 都是错误百出 当然 这对理解电影的剧情影响不大 但是 如果想通过看电影学英语的话 如此质量的字幕
  • linux 下各个4K区块文件大小测试速度对比 机械硬盘性能 64K性价比收益最高

    机械硬盘 每个区块取三次数最小值为准 带2G RAM缓存卡4K3 4 MB 秒8K7 3 MB 秒16K9 5 MB 秒32K16 7 MB 秒64K44 2 MB 秒128K67 1 MB 秒256K100 MB 秒512K139 MB
  • 爬虫的代理IP池写哪里了?

    亲爱的程序员小伙伴们 想要提高爬虫效率和稳定性 组建一个强大的代理IP池是非常重要的一步 今天我就来和你分享一下 代理IP池到底应该写在哪里 以及如何打造一个令人瞩目的代理IP池 准备好了吗 一起看看吧 一 代理IP池的代码位置选择 1 写
  • web基础与HTTP协议

    web基础与HTTP 一 web基础 1 DNS与域名 1 1域名 1 2 域名注册 1 3 DNS 解析 1 4 域名 二 网页的概念 1 网页 HTTP HTTPS 2 1网页基本概念 2 2 HTML 概述 2 2 1 HTML超文本
  • C++模板详解(函数模板、类模板)

    hello 这里是bangbang 今天来讲下模板 目录 1 泛型编程 2 函数模板 2 1 函数模板概念 2 2 函数模板格式 2 3 函数模板的实例化 2 4 模板参数的匹配原则 3 类模板 3 1 类模板定义格式 3 2 类模板实例化