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

【C++】初识模板

C++模板入门

    • 一、泛型编程
  • 二、函数模板
    • 1. 函数模板的概念
    • 2. 函数模板格式
    • 3. 函数模板的原理
    • 4. 函数模板的实例化
    • 5. 模板参数的匹配原则
  • 三、类模板

一、泛型编程

假设我们想实现一个交换函数,并且支持不同类型的参数实现,我们可以用 typedef 将类型进行重命名,例如以下代码:

		// 将 int 起别名为 DataTypetypedef int DataType;void Swap(DataType& x, DataType& y){DataType tmp = x;x = y;y = tmp;}int main(){DataType x = 0, y = 6;Swap(x, y);return 0;}

这样我们每次需要更换类型的时候,只需要更改 int 为其他类型即可;

以上是一种方法,还有一种方法可以使用函数重载实现,例如:

		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. 代码的可维护性比较低,一个出错可能所有的重载均出错。

那能否告诉编译器一个模板,让编译器根据不同的类型利用该模板来生成代码呢?答案是可以的,在这里就需要引入泛型编程,泛型编程: 编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

模板分为函数模板类模板

二、函数模板

1. 函数模板的概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

2. 函数模板格式

在定义函数模板之前,我们需要引入一个关键字:template,它是定义模板的关键字;

使用格式:template<typename T1, typename T2,......,typename Tn>,在 template 关键字后面要用尖括号括住模板参数,模板参数的数量可以是任意的,但是需要使用 typename 关键字来定义模板参数,也可以使用 class(切记:不能使用struct代替class)。

例如交换函数的函数模板:

		template<typename T>void Swap(T& t1, T& t2){T tmp = t1;t1 = t2;t2 = tmp;}

3. 函数模板的原理

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

例如下图就很好地体现了这一个过程:

在这里插入图片描述

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。

比如:当用 double 类型使用函数模板时,编译器通过对实参类型的推演,将 T 确定为 double 类型,然后产生一份专门处理 double 类型的代码,对于字符类型也是如此,即编译器用模板实例化生成对应的Swap 函数。

4. 函数模板的实例化

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

隐式实例化: 让编译器根据实参推演模板参数的实际类型,例如以下这个 Add 的函数模板,实现两个数的相加:

		template<class T>T Add(T a, T b){return a + b;}int main(){int a = 10, b = 20;double c = 1.11, d = 2.22;cout << "sum = " << Add(a, b) << endl;cout << "sum = " << Add(c, d) << endl;return 0;}

上面的两个调用实例化都没有问题,编译器进行了隐式实例化,运行的结果如下:

在这里插入图片描述

但是如果这样调用会编译通过吗:Add(a, d),答案是不行的,通过实参 a 将 T 推演为 int,通过实参 dT 推演为 double 类型,但模板参数列表中只有一个 T, 编译器无法确定此处到底该将 T 确定为 int 或者 double 类型而报错。

所以此时有两种解决方法:

  1. 用户自己来强制转化
  2. 使用显式实例化

如果自己来强制转化,就可以使用以下方法:

		int main(){int a = 10, b = 20;double c = 1.11, d = 2.22;cout << "sum = " << Add((double)a, d) << endl;cout << "sum = " << Add(a, (int)d) << endl;return 0;}

我们可以在调用 Add 函数时,将 a 强转为 double,或者将 d 强转为 int

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

例如上面的问题中,我们使用显式实例化解决,代码如下:

		int main(){int a = 10, b = 20;double c = 1.11, d = 2.22;cout << "sum = " << Add(a, b) << endl;cout << "sum = " << Add(c, d) << endl;cout << "sum = " << Add<double>(a, d) << endl;cout << "sum = " << Add<int>(a, d) << endl;return 0;}

我们在函数名的后面用尖括号指定了模板参数的类型,这就是显式实例化。

注意:如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

5. 模板参数的匹配原则

对于非模板函数同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

例如以下两段代码:

		// 专门处理int的加法函数int Add(int a, int b){cout << "int Add(int a, int b)" << endl;return a + b;}// 通用加法函数template<class T>T Add(T a, T b){cout << "T Add(T a, T b)" << endl;return a + b;}int main(){// 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的 Add 函数Add<int>(1, 2); return 0;}

三、类模板

假设我们我们需要实现一个通用的栈,我们可以使用 typedef 关键字对类型起别名,每次需要改变类型的时候,只需要在 typedef 更改即可,例如以下的 Stack 类:

		typedef int DataType;class Stack{public:Stack(size_t capacity = 4){_array = new DataType[capacity];_capacity = capacity;_size = 0;}~Stack(){cout << "~Stack()" << endl;delete[] _array;_array = nullptr;_size = _capacity = 0;}private:// 内置类型DataType* _array;int _capacity;int _size;};

虽然以上的 Stack 类不同的类型只需要改变 typedef 的类型即可,但是如果我同时需要两个栈,一个栈的参数是 int ,另一个栈的参数是 double 呢,上面的方法就不能很好地满足了,所以我们引入类模板。

类模板的使用如下,以 Stack 类为例:

		template<class T>class Stack{public:Stack(size_t capacity = 4){_array = new T[capacity];_capacity = capacity;_size = 0;}~Stack(){delete[] _array;_array = nullptr;_size = _capacity = 0;}private:T* _array;int _capacity;int _size;};

实例化对象如下:

		int main(){Stack<int> st1;Stack<double> st2;return 0;}

注意,Stack 是类名,Stack<int>Stack<double> 才是类型;template 的作用范围是 Stack 这个类。

这样我们就同时实现了两个栈,一个栈存放的参数是 int,另外一个存放的是 double

相关文章:

【C++】初识模板

C模板入门 一、泛型编程 二、函数模板1. 函数模板的概念2. 函数模板格式3. 函数模板的原理4. 函数模板的实例化5. 模板参数的匹配原则 三、类模板 一、泛型编程 假设我们想实现一个交换函数&#xff0c;并且支持不同类型的参数实现&#xff0c;我们可以用 typedef 将类型进行重…...

学习Pull request

我从我的导师Xing Fan指导和帮助&#xff0c;利用我的导师chunlong Li提供ChatGPT&#xff0c;在百度搜索&#xff0c;学习一些资料。以下很多内容都是我的导师Xing Fan做的。谢谢Xing Fan。考虑到隐私&#xff0c;不适合截图公开。 第一步&#xff1a; 打开Git Bash Here 如…...

python爬虫实战(1)--爬取新闻数据

想要每天看到新闻数据又不想占用太多时间去整理&#xff0c;萌生自己抓取新闻网站的想法。 1. 准备工作 使用python语言可以快速实现&#xff0c;调用BeautifulSoup包里面的方法 安装BeautifulSoup pip install BeautifulSoup完成以后引入项目 2. 开发 定义请求头&#xf…...

React Hooks 详细使用介绍

useState 状态管理 useState 是 React 中的一个基础 Hook&#xff0c;允许你在不使用 class 组件的情况下管理组件状态。 参数 初始值 你可以直接传递状态的初始值给 useState&#xff1a; const [name, setName] useState("John");使用函数设置初始值 当初始…...

python版《羊了个羊》游戏开发第一天

Python小型项目实战教学课《羊了个羊》 一、项目开发大纲&#xff08;初级&#xff09; 版本1.0&#xff1a;基本开发 课次 内容 技术 第一天 基本游戏地图数据 面向过程 第二天 鼠标点击和移动 面向对象 第三天 消除 设计模式&#xff1a;单例模式 第四天 完整…...

【uniapp】原生子窗体subNvue的使用与踩坑

需求 最近接到个需求, 需要在video组件上弹出弹窗, 也就是覆盖video这个原生组件 未播放时, 弹窗可以覆盖, 但是当video播放时, 写的弹窗就覆盖不了了 因为video是原生组件, 层级非常高, 普通标签是覆盖不了的, map标签同理 覆盖原生组件, 官方给出解决办法一. 使用cover-view…...

浅析 C 语言的共用体、枚举和位域

前言 最近在尝试阅读一些系统库的源码&#xff0c;但是其中存在很多让我感到既熟悉又陌生的语法。经过资料查阅&#xff0c;发现是 C 语言中的共用体和位域。于是&#xff0c;趁着课本还没有扔掉&#xff0c;将一些相关的知识点记录在本文。 文章目录 前言共用体 (union)枚举…...

TartanVO: A Generalizable Learning-based VO 论文阅读

论文信息 题目:TartanVO: A Generalizable Learning-based VO 作者&#xff1a;Wenshan Wang&#xff0c; Yaoyu Hu 来源&#xff1a;ICRL 时间&#xff1a;2021 代码地址&#xff1a;https://github.com/castacks/tartanvo Abstract 我们提出了第一个基于学习的视觉里程计&…...

单例模式-java实现

介绍 单例模式的意图&#xff1a;保证某个类在系统中有且仅有一个实例。 我们可以看到下面的类图&#xff1a;一般的单例的实现&#xff0c;是属性中保持着一个自己的私有静态实例引用&#xff0c;还有一个私有的构造方法&#xff0c;然后再开放一个静态的获取实例的方法给外界…...

篇八:装饰器模式:动态增加功能

篇八&#xff1a;“装饰器模式&#xff1a;动态增加功能” 开始本篇文章之前先推荐一个好用的学习工具&#xff0c;AIRIght&#xff0c;借助于AI助手工具&#xff0c;学习事半功倍。欢迎访问&#xff1a;http://airight.fun/。 另外有2本不错的关于设计模式的资料&#xff0c…...

算法通关村第五关——n数之和问题解析

1. 两数之和问题 力扣第1题就是两数之和问题&#xff0c;给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那两个整数&#xff0c;并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一…...

小白到运维工程师自学之路 第七十集 (Kubernetes集群部署)

一、概述 Kubernetes&#xff08;简称K8S&#xff09;是一个开源的容器编排和管理平台&#xff0c;是由Google发起并捐赠给Cloud Native Computing Foundation&#xff08;CNCF&#xff09;管理的项目。它的目标是简化容器化应用的部署、扩展、管理和自动化操作。 以下是Kube…...

docker 部署mysql 5.6集群

docker搭建mysql的集群&#xff08;一主双从&#xff09; 1.拉取镜像 docker pull mysql:5.6 2.启动master容器 docker run -it -d --name mysql_master -p 3306:3306 --ip 192.168.162.100 \ -v /data/mysql_master/mysql:/var/lib/mysql \ -v /data/mysql_master/conf.d…...

mysql基本信息查询

1.查看mysql表的数据量 select table_schema as 数据库, table_name as 表名, table_rows as 记录数, truncate(data_length/1024/1024, 2) as 数据容量(MB), truncate(index_length/1024/1024, 2) as 索引容量(MB) from information_schema.tables order by data_length des…...

C语言初学者必读:使用for循环将数字从大到小排序并输出

在学习C语言编程的过程中&#xff0c;了解数组的输入和排序是非常基础且重要的一部分。本文将以通俗易懂的方式&#xff0c;教你如何使用for循环实现将输入的n个数字按照从大到小的顺序输出&#xff0c;帮助你逐步掌握数组的使用和排序算法。 第一步&#xff1a;获取用户输入 …...

【Vue+Element-plus】记录后台首页多echart图静态页面

一、页面效果 二、完整代码 Index.vue <template><div><div><DateTime /><!-- {{username}} --></div><el-row :gutter"20"><el-col :span"8"><div class"grid-content bg-purple"><P…...

BM5 合并k个已排序的链表 javascript

描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围&#xff1a; 示例1 输入&#xff1a; [{1,2,3},{4,5,6,7}] 返回值&#xff1a; {1,2,3,4,5,6,7}示例2 输入&#xff1a; [{1,2},{1,4,5},{6}] 返回值&#xff1a; {1,1,2,4,5,6}解题思路 利用两个…...

1.利用matlab建立符号表达式(matlab程序)

1.简述 、 1. 使用sym命令创建符号变量和表达式 语法&#xff1a; sym(‘变量’,参数) %把变量定义为符号对象 说明&#xff1a;参数用来设置限定符号变量的数学特性&#xff0c;可以选择为’positive’、’real’和’unreal’&#xff0c; ’positive’ 表示为“正、实”符…...

LVS工作环境配置

一、LVS-DR工作模式配置 模拟环境如下&#xff1a; 1台客户机 1台LVS负载调度器 2台web服务器 1、环境部署 &#xff08;1&#xff09;LVS负载调度器 yum install -y ipvsadm # 在LVS负载调度器上进行环境安装 ifconfig ens33:200 192.168.134.200/24 # 配置LVS的VIP…...

金蝶,「起舞」在大模型时代

在过去的几年时间里&#xff0c;基于EBC的平台能力&#xff0c;金蝶已经走出了一个新的进化之路&#xff0c;这条路是对自身产品竞争力的重新构建&#xff0c;也更是对企业数字化转型需求的更大程度满足。 如今&#xff0c;苍穹GPT大模型更是让这种竞争力和服务力更向前一步。…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...

【实施指南】Android客户端HTTPS双向认证实施指南

&#x1f510; 一、所需准备材料 证书文件&#xff08;6类核心文件&#xff09; 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…...