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

C++模板—函数模板、类模板

 目录

一、函数模板

1、概念

2、格式

3、实例化

4、模板参数的匹配 

二、类模板

1、定义格式

2、实例化


交换两个变量的值,针对不同类型,我们可以使用函数重载实现。
void Swap(double& left, double& right)
{double tmp = left;left = right;right = tmp;
}
void Swap(int& left, int& right)
{int tmp = left;left = right;right = tmp;
}
使用函数重载虽然可以实现,但是有一下几个不好的地方:
  • 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数。
  • 代码的可维护性比较低,一个出错可能所有的重载均出错。

C++中提供了一种新的方式——泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

一、函数模板

1、概念

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

2、格式

template<typename T1, typename T2,......,typename Tn>

返回值类型 函数名(参数列表){ },typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)。

交换变量值的函数使用函数模板如下:

template<class T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}

简化过程的模板实际上是编译器帮我们处理了复杂的过程。

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

 

 定义多个模板数:

template<class A,class B>
void Fun{}

其实库里包含了swap函数,我们以后可以直接用。

int main()
{int a = 1, b = 2;swap(a, b);double c = 1.1, d = 2.22;swap(a, b);return 0;
}

3、实例化

函数模板的实例化是指根据函数模板创建具体的函数实例,实例化函数模板的过程是将函数模板中的类型参数替换为实际的类型,并生成对应的函数定义。这样就可以根据不同的类型参数创建多个函数实例,每个实例都可以处理相应类型的数据。

模板参数实例化分为:隐式实例化和显式实例化
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.11, d2 = 20.22;Add(a1, a2);Add(d1, d2);Add(a1, d1);return 0;
}

前两个相同类型可以正常编译,但模板参数类型不同时编译出现错误。

这时因为在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T, 编译器无法确定此处到底该将T确定为int 或者 double类型而报错,在模板中,编译器一般不会进行类型转换操作。

我们可以选择强制类型转换解决,或者显示实例化。

 方法一:显式类型转换,实参传递给形参,自动推演模板类型。

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.11, d2 = 20.22;cout << Add(a1, (int)d1) << endl;//显示类型转换cout << Add((double)a1, d1) << endl;return 0;
}

 

方法二:显示实例化,在函数名和参数列表中间加上模板参数,参数隐式类型转换。

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

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.11, d2 = 20.22;cout << Add<int>(a1, d1) << endl;//隐式类型转换cout << Add<double>(a1, d1) << endl;return 0;
}

4、模板参数的匹配 

1、一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。

下面这两个函数是可以同时存在的。 

int Add(int left, int right)
{return left + right;
}template<class S>
S Add(S left,S right)
{return left + right;
}int main()
{Add(1, 2); return 0;
}

 在Add函数调用处打断点,我们试着观察一下到底会调用哪个函数。

 

 可以发现,Add函数选择了专门处理int的加法函数。

 

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

我们也可以通过显示实例化调用函数模板。 

 

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

  • 在函数模板中,对于函数参数的类型推断是严格按照实参的类型进行匹配的。如果实参的类型与函数模板参数的类型不完全匹配,编译器将无法进行自动类型转换来匹配函数模板的参数类型。
  • 相比之下,普通函数可以进行自动类型转换。当调用普通函数时,如果实参的类型与函数参数的类型不完全匹配,编译器会尝试进行自动类型转换,以便匹配函数参数的类型。这种自动类型转换可以是隐式的,也可以是通过类型转换操作符进行显式的。

二、类模板

 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` 是一个类模板,它并不是一个具体的类,而是一个用于生成具体类的模具或蓝图。当我们使用 `Vector` 类模板时,需要提供具体的类型参数,例如 `Vector<int>` 或 `Vector<double>`,编译器会根据这些类型参数生成对应的具体类。

类模板的定义中使用了模板参数 `T`,它表示一个占位符类型,可以在实例化时被具体的类型替换。在这个例子中,`T` 表示动态顺序表中存储的元素类型。

通过实例化类模板,编译器会根据模板定义生成具体的类,其中的成员函数和成员变量的类型会被替换为实际的类型。例如,`Vector<int>` 实例化后的类将具有 `int* _pData`、`size_t _size` 和 `size_t _capacity` 成员变量,以及相应的成员函数。

因此,`Vector` 并不是一个具体的类,而是一个用于生成具体类的模板。每次使用不同的类型参数实例化 `Vector`,都会生成一个独立的具体类,用于处理特定类型的数据。这样可以提供代码的灵活性和重用性,使得我们可以使用相同的代码逻辑处理不同类型的数据。

2、实例化

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

相关文章:

C++模板—函数模板、类模板

目录 一、函数模板 1、概念 2、格式 3、实例化 4、模板参数的匹配 二、类模板 1、定义格式 2、实例化 交换两个变量的值&#xff0c;针对不同类型&#xff0c;我们可以使用函数重载实现。 void Swap(double& left, double& right) {double tmp left;left ri…...

Monkey

一、Monkey的概念 “猴子测试”是指没有测试经验的人甚至对计算机根本不了解的人&#xff08;就像猴子一样&#xff09;不需要知道程序的任何用户交互方面的知识&#xff0c;如果给他一个程序&#xff0c;他就会针对他看到的界面进行操作&#xff0c;其操作是无目的的、乱点乱按…...

SQL中left join、right join、inner join等的区别

一张图可以简洁明了的理解出left join、right join、join、inner join的区别&#xff1a; 1、left join 就是“左连接”&#xff0c;表1左连接表2&#xff0c;以左为主&#xff0c;表示以表1为主&#xff0c;关联上表2的数据&#xff0c;查出来的结果显示左边的所有数据&#…...

算法学习—排序

排序算法 一、选择排序 1.算法简介 选择排序是一个简单直观的排序方法&#xff0c;它的工作原理很简单&#xff0c;首先从未排序序列中找到最大的元素&#xff0c;放到已排序序列的末尾&#xff0c;重复上述步骤&#xff0c;直到所有元素排序完毕。 2.算法描述 1&#xff…...

在Pycharm中创建项目新环境,安装Pytorch

在python项目中&#xff0c;很多项目使用的各类包的版本是不一致的。所以我们可以对每个项目有专属于它的环境。所以这个文章就是教你如何创建新环境。 一、创建新环境 首先我们需要去官网下载conda。然后在Pycharm下面添加conda的可执行文件。 用conda创建新环境。 二、…...

linux里source、sh、bash、./有什么区别

1、source source a.sh 在当前shell内去读取、执行a.sh&#xff0c;而a.sh不需要有"执行权限" source命令可以简写为"." . a.sh 注意&#xff1a;中间是有空格的。 2、sh/bash sh a.sh bash a.sh 都是打开一个subshell去读取、执行a.sh&#xff0c;而a.…...

IDEA编译器技巧-提示词忽略大小写

IDEA编译器技巧-提示词忽略大小写 写代码时,每次创建对象都要按住 Shift 字母 做大写开头, 废手, 下面通过编译器配置解放Shift 键 setting -> Editor -> General -> Code Completion -> Match case 把这个√去掉, 创建对象就不需要再按住 Shift 键 示例: 1.…...

【MySQL】MySQL安装 环境初始化

MySQL安装 MYSQL官网 安装完成后,傻瓜下一步即可 配置一下环境变量即可 (1) 初始化MySQL, 管理员身份运行 mysqld --initialize-insecure(2) 注册 mysqld mysqld -install# 如果记录以前的版本执行下面指令 mysqld -remove(3) 启动MySQL服务 // 启动mysql服务 net start …...

C# IList 与List区别二叉树的层序遍历

IList 接口&#xff1a; IList 是一个接口&#xff0c;定义了一种有序集合的通用 API。继承自 ICollection 接口和IEnumerable<T>&#xff0c;是所有泛型列表的基接&#xff0c;口它提供了对列表中元素的基本操作&#xff0c;如添加、删除、索引访问等。IList 不是一个具…...

助力android面试2024【面试题合集】

转眼间&#xff0c;2023年快过完了。今年作为口罩开放的第一年大家的日子都过的十分艰难&#xff0c;那么想必找工作也不好找&#xff0c;在我们android开发这一行业非常的卷&#xff0c;在各行各业中尤为突出。android虽然不好过&#xff0c;但不能不吃饭吧。卷归卷但是还得干…...

【动态规划】LeetCode-62.不同路径

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…...

对 Vision Transformers 及其基于 CNN-Transformer 的变体的综述

A survey of the Vision Transformers and its CNN-Transformer based Variants 摘要1、介绍2、vit的基本概念2.1 patch嵌入2.2 位置嵌入2.2.1 绝对位置嵌入(APE)2.2.2 相对位置嵌入(RPE)2.2.3卷积位置嵌入(CPE) 2.3 注意力机制2.3.1多头自我注意(MSA) 2.4 Transformer层2.4.1 …...

MongoDB简介

数据库&#xff0c;顾名思义&#xff0c;是保存数据的地方。中华文化博大精深&#xff0c;短短3个文字&#xff0c;就定义了一个强大的数据管理和读写方式出来。数据库&#xff0c;管理的对象是数据。称为库&#xff0c;表示数据在库中有组织&#xff0c;相互之间有微妙的关系。…...

尚硅谷hadoop3.x课程部分资料文件下载,jdk,hadoopjar包

jdk文件百度云下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1MCiGRzOZY8rAFpRJwA3tdw 提取码&#xff1a;kphl hadoop的jar包&#xff1a; 最新版官网链接&#xff1a; Index of /dist/hadoop/core/stable (apache.org) 百度云下载&#xff0c;3.3.3版&#xf…...

vue el-radio-group多选封装及使用

基于Element UI库的Vue组件&#xff0c;实现了一个单选/多选框组合的效果&#xff0c;可以根据 type 属性的不同值来切换单选框&#xff08;默认&#xff09;和按钮式单选框/多选框。 创建组件index.vue (src/common-ui/radioGroup/index.vue) <template><el-radio-g…...

Kaggle-水果图像分类银奖项目 pytorch Densenet GoogleNet ResNet101 VGG19

一些原理文章 卷积神经网络基础&#xff08;卷积&#xff0c;池化&#xff0c;激活&#xff0c;全连接&#xff09; - 知乎 PyTorch 入门与实践&#xff08;六&#xff09;卷积神经网络进阶&#xff08;DenseNet&#xff09;_pytorch conv1x1_Skr.B的博客-CSDN博客GoogLeNet网…...

TPLink-Wr702N 通过OpenWrt系统打造打印服务器实现无线打印

最近淘到了一个TPLink-Wr702N路由器&#xff0c;而且里面已经刷机为OpenWrt系统了&#xff0c;刚好家里有一台老的USB打印机&#xff0c;就想这通过路由器将打印机改为无线打印机&#xff0c;一番折腾后&#xff0c;居然成功了&#xff0c;这里记录下实现过程&#xff0c;为后面…...

[UGUI]实现从一个道具栏拖拽一个UI道具到另一个道具栏

在Unity游戏开发中&#xff0c;实现UI道具的拖拽功能是一项常见的需求。本文将详细介绍如何使用Unity的UGUI系统和事件系统&#xff0c;实现从一个道具栏拖拽一个UI道具到另一个道具栏的功能。 一、准备工作 首先&#xff0c;你需要在Unity中创建两个道具栏和一些UI道具。道具…...

微服务--08--Seata XA模式 AT模式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 分布式事务Seata 1.XA模式1.1.两阶段提交1.2.Seata的XA模型1.3.优缺点 AT模式2.1.Seata的AT模型2.2.流程梳理2.3.AT与XA的区别 分布式事务 > 事务–01—CAP理论…...

Doris 数据导入一:Broker Load 方式

1.Doris导入数据的方式总结 导入(Load)功能就是将用户的原始数据导入到 Doris 中。导入成功后,用户即可通过 Mysql 客户端查询数据。为适配不同的数据导入需求,Doris 系统提供了6种不同的导入方式。每种导入方式支持不同的数据源,存在不同的使用方式(异步,同步)。 所有…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...