C++--模板(template)详解—— 函数模板与类模板

news2024/9/23 6:31:53

目录

1.泛型编程

2.函数模板

2.1 函数模板概念

2.2 函数模板格式

2.3 函数模板的原理

2.4 函数模板的实例化

2.5 模板参数的匹配原则

3.类模板

3.1 类模板的定义格式

3.2 类模板的实例化


1.泛型编程

在C中如果让你写一个交换函数,应该怎么做呢?

//用于整型的交换
void Swap1(int* x, int* y)
{
	int* tmp = *x;
	*x = *y;
	*y = *tmp;
}

//用于双精度浮点数的交换
void Swap2(double *x, double *y)
{
	double *tmp = *x;
	*x = *y;
	*y = *tmp;
}

在C中需要使用指针来完成对两个数的交换,而且函数名也必须不同

在C++中我们学习了引用加重载,现在我们使用C++来试一下

//用于整型的交换
void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

//用于双精度浮点数的交换
void Swap(double& x, double& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}

可以看到学了重载和引用实现两个数的交换依旧很麻烦,参数类型不同必然需要我们写出不同的函数来实现x , y的交换,在C++中依靠重载引用实现这种操作,但是有很多不好的的地方

1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数

2. 代码的可维护性比较低,一个出错可能所有的重载均出错

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

就像活字印刷术和造纸术一样,我们可以依靠这两样东西在不同的纸上刻不同的内容,C++中的摸具就好比造纸术一样,每一张都是同一摸具制作出,但每一张纸都是不一样的内容

在二十多年前,我们C++的鼻祖引入了模板(template)这个概念,模板是C++语言重要组成之一

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

2.函数模板

2.1 函数模板概念

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

2.2 函数模板格式

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

返回值类型 函数名(参数列表)

{

        //函数体

}

template<typename T>

void Swap(T& x, T& y)
{
	T temp = x;
	x = y;
	y = temp;
}

int main()
{
    //函数模板实例化生成具体函数
    //函数模板根据调用,自己推导模板参数的类型,实例化出对应的函数
	int a = 10, b = 12;
	Swap(a, b);
	cout << a << ' ' << b << '\n';

	double c = 10.10, d = 20.20;
	Swap(c, d);
	cout << c << ' ' << d << '\n';

	swap(c, d);
	return 0;
}

使用模板,我们只需要一个函数就能解决上面的问题

因为swap是很常用的类型所以库里面它帮你已经写好了,swap函数直接使用就可以了

int main()
{
    //函数模板实例化生成具体函数
    //函数模板根据调用,自己推导模板参数的类型,实例化出对应的函数
	int a = 10, b = 12;
	swap(a, b);
	cout << a << ' ' << b << '\n';

	double c = 10.10, d = 20.20;
	swap(c, d);
	cout << c << ' ' << d << '\n';

	swap(c, d);
	return 0;
}

tips:

typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)

2.3 函数模板的原理

那么如何解决上面的问题呢?大家都知道,瓦特改良蒸汽机,人类开始了工业革命,解放了生产力。机器生 产淘汰掉了很多手工产品。本质是什么,重复的工作交给了机器去完成。有人给出了论调:懒人创造世界。

马云:世界是懒人创造的

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

编译器,很辛苦,哈哈哈

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

2.4 函数模板的实例化

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

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

template <class T>

T Add(const T& left, const T& right)
{
	return left + right;
}

int main()
{
	Add(1, 9); 
	Add(2, 8);

	return 0;
}

tips:以下这段代码不会通过编译

int main()
{
    int a = 10;
    double b = 20.2;
    Add(a, b);
    return 0;
}

在编译器编译期间,在模板实例化的时候,编译器需要推理a和b的类型,编译器将a的类型推演为int,b为double,但是模板只有一个参数T,即:编译器无法推断T的类型为int还是double,所以报错

注意:

在模板中,编译器一般不会自动转换类型,因为一旦错了,编译器就需要背黑锅

这里有两种解决方式:

1.使用强制类型转换,将这两个参数的类型转换为同一个类型

例:Add(a, (int)b);

2.就是我们下面要将的显式实例化

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

int main(void)
{
	int a = 10;
	double b = 20.2;
	// 显式实例化
	Add<int>(a, b);
	return 0;
}

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

使用场景:

template <class T>
T* Alloc(int n)
{
	return new T[n];
}

int main()
{
    double* p1 = Alloc<double>(10);    

    return 0;
}

需要传递形参n来申请空间,这里就必须要使用显式实例化

2.5 模板参数的匹配原则

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

//非模板函数,int类型的Add加法函数
int Add(int x, int y)
{
	return x + y;
}

template <typename T1,typename T2>

//模板函数,通用类型Add加法函数
T Add(const T1& x, const T2& y)
{
	return x + y;
}

int main()
{
	Add(10, 20); //调用int类型的Add加法函数,编译器不会去实例化
	Add<int>(10, 20); //调用通用类型的Add加法函数,编译器会去实例化

	return 0;
}

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

//非模板函数,int类型的Add加法函数
int Add(int x, int y)
{
	return x + y;
}

template <typename T1, typename T2>

//模板函数,通用类型Add加法函数
T Add(const T1& x, const T2& y)
{
	return x + y;
}

int main()
{
	int a = 10, b = 20;

	Add(10, 20); //与非模板函数更加吻合,编译器会使用int Add函数
	Add(10, 20.0); //编译器会选择模板函数T Add,模板函数会根据需求生成更加匹配的Add函数
    
	return 0;
}

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

template<typename T>

T Add(const T& x, const T& y)
{
	return x + y;
}

int main()
{
	int sum = Add(10, 10.5);

	return 0;
}

编译器不会帮你自动类型转换,将10转换为10.0,或10.5转换为10

3.类模板

3.1 类模板的定义格式

template<class T1, class T2, class T3 .... class Tn>

class 类模板名

{

        // 类内成员定义

};

类成员函数声明和定义分离

template<class T>
class Date
{
public:
	Date();
	void Print();
private:
	int _year;
	int _month;
	int _day;
};

//普通类:类名和类型一样
//类模板:类名和类型不一样
//类模板 类名:Date 类型:Date<T>  (类型是类名加<模板参数(不用加class或typename)>)

template<class T>
Date<T>::Date()
	:_year(1)
	,_month(1)
	,day(1)
{

}

template<class T>
void Date<T>::Print()
{
	cout << _year << endl;
	cout << _month << endl;
	cout << _day << endl;
}

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

3.2 类模板的实例化

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

template<class T1, class T2>
class sroce
{
public:
	sroce(const T1& math, const T2& English);
	
	void Print();
private:
	T1 _math;
	T2 _English;
};

template<class T1, class T2>
sroce<T1, T2>::sroce(const T1& math, const T2& English) 
	:_math(math)
	,_English(English)
{}

template<class T1, class T2>
void sroce<T1, T2>::Print()
{
	cout << _math << '\n';
	cout << _English << '\n';
}

int main()
{
	sroce<int, double> d1(10, 10.5);
	d1.Print();

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2156876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树进阶【c++实现】【二叉搜索树的实现】

目录 二叉树进阶1.二叉搜索树1.1二叉搜索树的实现1.1.1二叉搜索树的查找1.1.2二叉搜索树的插入1.1.3中序遍历(排序)1.1.4二叉搜索树的删除(重点) 1.2二叉搜索树的应用1.2.1K模型1.2.2KV模型 1.3二叉搜索树的性能分析 二叉树进阶 前言&#xff1a; map和set特性需要先铺垫二叉搜…

Python3网络爬虫开发实战(16)分布式爬虫(第一版)

文章目录 一、分布式爬虫原理1.1 分布式爬虫架构1.2 维护爬取队列1.3 怎样来去重1.4 防止中断1.5 架构实现 二、Scrapy-Redis 源码解析2.1 获取源码2.2 爬取队列2.3 去重过滤2.4 调度器 三、Scrapy 分布式实现3.1 准备工作3.2 搭建 Redis 服务器3.3 部署代理池和 Cookies 池3.4…

超越sora,最新文生视频CogVideoX-5b模型分享

CogVideoX-5B是由智谱 AI 开源的一款先进的文本到视频生成模型&#xff0c;它是 CogVideoX 系列中的更大尺寸版本&#xff0c;旨在提供更高质量的视频生成效果。 CogVideoX-5B 采用了 3D 因果变分自编码器&#xff08;3D causal VAE&#xff09;技术&#xff0c;通过在空间和时…

【OpenAI o1背后技术】Sef-play RL:LLM通过博弈实现进化

【OpenAI o1背后技术】Sef-play RL&#xff1a;LLM通过博弈实现进化 OpenAI o1是经过强化学习训练来执行复杂推理任务的新型语言模型。特点就是&#xff0c;o1在回答之前会思考——它可以在响应用户之前产生一个很长的内部思维链。也就是该模型在作出反应之前&#xff0c;需要…

简单题104. 二叉树的最大深度 (python)20240922

问题描述&#xff1a; python&#xff1a; # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution(object…

Python 入门(一、使用 VSCode 开发 Python 环境搭建)

Python 入门第一课 &#xff0c;环境搭建...... by 矜辰所致前言 现在不会 Python &#xff0c;好像不那么合适&#xff0c;咱先不求精通&#xff0c;但也不能不会&#xff0c;话不多说&#xff0c;开干&#xff01; 这是 Python 入门第一课&#xff0c;当然是做好准备工作&a…

论前端框架的对比和选择 依据 前端框架的误区

前端框架的对比和选择依据 在前端开发中&#xff0c;有多种框架可供选择&#xff0c;以下是一些常见前端框架的对比和选择依据&#xff1a; 一、Vue.js 特点&#xff1a; 渐进式框架&#xff0c;灵活度高&#xff0c;可以逐步引入到项目中。学习曲线相对较平缓&#xff0c;容…

Java项目实战II基于Java+Spring Boot+MySQL的民宿在线预定平台(开发文档+源码+数据库)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在旅游市场…

强大的重命名工具 | Bulk Rename Utility v4.0 便携版

软件简介 Bulk Rename Utility是一款功能强大且易于使用的文件批量重命名工具。它不仅体积小巧&#xff0c;而且完全免费&#xff0c;提供了友好的用户界面。该软件允许用户对文件或文件夹进行批量重命名&#xff0c;支持递归操作&#xff0c;即包含子文件夹的重命名。 软件特…

Apache Iceberg 概述

Apache Iceberg概述 一、what is Apache Iceberg&#xff1f; 为了解决数据存储和计算引擎之间的适配的问题&#xff0c;Netflix开发了Iceberg&#xff0c;2018年11月16日进入Apache孵化器&#xff0c;2020 年5月19日从孵化器毕业&#xff0c;成为Apache的顶级项目。 Apache…

SpringBoot实战(三十)发送HTTP/HTTPS请求的五种实现方式【下篇】(Okhttp3、RestTemplate、Hutool)

目录 一、五种实现方式对比结果二、Demo接口地址实现方式三、Okhttp3 库实现3.1 简介3.2 Maven依赖3.3 配置文件3.4 配置类3.5 工具类3.6 示例代码3.7 执行结果实现方式四、Spring 的 RestTemplate 实现4.1 简介4.2 Maven依赖4.3 配置文件4.4 配置类4.5 HttpClient 和 RestTemp…

华为HarmonyOS灵活高效的消息推送服务(Push Kit) - 5 发送通知消息

场景介绍 通知消息通过Push Kit通道直接下发&#xff0c;可在终端设备的通知中心、锁屏、横幅等展示&#xff0c;用户点击后拉起应用。您可以通过设置通知消息样式来吸引用户。 开通权益 Push Kit根据消息内容&#xff0c;将通知消息分类为服务与通讯、资讯营销两大类别&…

idea2021git从dev分支合并到主分支master

1、新建分支 新建一个名称为dev的分支&#xff0c;切换到该分支下面&#xff0c;输入新内容 提交代码到dev分支的仓库 2、切换分支 切换到主分支&#xff0c;因为刚刚提交的分支在dev环境&#xff0c;所以master是没有 3、合并分支 点击push&#xff0c;将dev里面的代码合并到…

Spring AI Alibaba,阿里的AI Java 开发框架

源码地址 https://github.com/alibaba/spring-ai-alibaba

资源创建方式-Job

Job: 容器按照持续运行的时间可分为两类&#xff0c;服务类容器&#xff0c;和工作类容器 服务类容器通常持续提供服务&#xff0c;需要一直运行&#xff0c;比如HTTP,Server&#xff0c;Daemon等&#xff0c; 工作类容器则是一次性任务&#xff0c;比如批处理程序&#xff0…

跟着问题学12——GRU详解

1 GRU 1. 什么是GRU GRU&#xff08;Gate Recurrent Unit&#xff09;是循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;的一种。和LSTM&#xff08;Long-Short Term Memory&#xff09;一样&#xff0c;也是为了解决长期记忆 和反向传播中的梯度等问题…

数据集-目标检测系列-吸烟检测数据集 smoking cigarette >> DataBall

数据集-目标检测系列-吸烟检测数据集 smoking cigarette >> DataBall 数据集-目标检测系列-吸烟检测数据集 &#xff08;smoking cigarette&#xff09; 数据量&#xff1a;1W 想要进一步了解&#xff0c;请联系 DataBall。 DataBall 助力快速掌握数据集的信息和使用方…

闯关leetcode——67. Add Binary

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/add-binary/description/ 内容 Given two binary strings a and b, return their sum as a binary string. Example 1: Input: a “11”, b “1” Output: “100” Example 2: Input: a “101…

Qt系统相关——事件

文章目录 事件和信号槽的关系事件处理鼠标事件鼠标进入和离开鼠标点击获取位置鼠标释放鼠标双击鼠标移动鼠标滚轮 键盘事件定时器事件窗口移动和窗口改变 事件和信号槽的关系 Qt信号槽机制&#xff1a; 用户进行的操作就可能产生信号&#xff0c;可以给某个信号指定槽函数&…

Effective Java 学习笔记 如何为方法编写文档

目录 方法的文档注解设计的原则 Javadoc常用的文档注释 一些注意细节 通过Javadoc命令生成h5页面 这是第8章Java方法的最后一部分&#xff0c;聚焦为导出的API编写文档注释。 如果要想使得API真正可用&#xff0c;配套的文档是必须的。Java提供了Javadoc这个文档生成工具&…