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

目录

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.mfbz.cn/a/882058.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构与算法 | 灵神题单 | 二叉搜索树篇】力扣99, 1305, 230, 897

1. 力扣99&#xff1a;恢复二叉搜索树 1.1 题目&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下&#xff0c;恢复这棵树 。 示例 1&#xff1a; 输入&#xff1a;root [1,3,null,null,2] 输出&…

EMT-LTR--学习任务间关系的多目标多任务优化

EMT-LTR–学习任务间关系的多目标多任务优化 title&#xff1a; Learning Task Relationships in Evolutionary Multitasking for Multiobjective Continuous Optimization author&#xff1a; Zefeng Chen, Yuren Zhou, Xiaoyu He, and Jun Zhang. journal&#xff1a; IEE…

240922-chromadb的基本使用

A. 背景介绍 ChromaDB 是一个较新的开源向量数据库&#xff0c;专为高效的嵌入存储和检索而设计。与其他向量数据库相比&#xff0c;ChromaDB 更加专注于轻量化、简单性和与机器学习模型的无缝集成。它的核心目标是帮助开发者轻松管理和使用高维嵌入向量&#xff0c;特别是与生…

【计算机网络 - 基础问题】每日 3 题(十八)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

通信工程学习:什么是NFV网络功能虚拟化

NFV&#xff1a;网络功能虚拟化 NFV&#xff08;Network Function Virtualization&#xff09;&#xff0c;即网络功能虚拟化&#xff0c;是一种通过虚拟化技术实现网络功能的技术手段。它借鉴了x86服务器的架构&#xff0c;将传统的网络硬件设备如路由器、交换机、防火墙、负载…

华为eNSP使用详解

eNSP&#xff08;Enterprise Network Simulation Platform&#xff09;是华为提供的一款网络仿真平台&#xff0c;它允许用户在没有真实设备的情况下进行网络实验和学习网络技术。eNSP可以模拟各种网络设备&#xff0c;如交换机、路由器、防火墙等&#xff0c;并支持创建多种网…

【python】【绘制小程序】动态爱心绘制

背景介绍 参考链接&#xff1a;https://blog.csdn.net/Python_HUHU/article/details/139703289点的背景颜色在开始修改&#xff1b;文字的颜色在最后修改。文字内容可以修改。 python 代码 import tkinter as tk import random from math import sin, cos, pi, log from PIL…

VMware ESXi 8.0U3b macOS Unlocker OEM BIOS 2.7 集成网卡驱动和 NVMe 驱动 (集成驱动版)

VMware ESXi 8.0U3b macOS Unlocker & OEM BIOS 2.7 集成网卡驱动和 NVMe 驱动 (集成驱动版) 发布 ESXi 8.0U3 集成驱动版&#xff0c;在个人电脑上运行企业级工作负载 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-8-u3-sysin/&#xff0c;查看最新版…

数字IC设计\FPGA 职位经典笔试面试整理--基础篇1

注&#xff1a; 资料都是基于网上一些博客分享和自己学习整理而成的 1&#xff1a;什么是同步逻辑和异步逻辑&#xff1f; 同步逻辑是时钟之间有固定的因果关系。异步逻辑是各时钟之间没有固定的因果关系。 同步时序 逻辑电路的特点&#xff1a;各触发器的时钟端全部连接在一…

基于机器学习的注意力缺陷/多动障碍 (ADHD)(python论文+代码)HYPERAKTIV

简述 医疗保健领域的机器学习研究往往缺乏完全可重复性和可比性所需的公共数据。由于患者相关数据附带的隐私问题和法律要求&#xff0c;数据集往往受到限制。因此&#xff0c;许多算法和模型发表在同一主题上&#xff0c;没有一个标准的基准。因此&#xff0c;本文提出了一个公…

【STM32】TIM定时器定时中断与定时器外部时钟的使用

TIM定时器定时中断与定时器外部时钟的使用 一、TIM定时器简介1、TIM&#xff08;Timer&#xff09;定时器2、定时器类型3、高级定时器4、通用定时器5、基本定时器6、定时中断基本结构代码编写&#xff1a;定时中断/外部时钟定时中断 7、预分频器时序8、计数器时序9、计数器无预…

Arthas dashboard(当前系统的实时数据面板)

文章目录 二、命令列表2.1 jvm相关命令2.1.1 dashboard&#xff08;当前系统的实时数据面板&#xff09; 二、命令列表 2.1 jvm相关命令 2.1.1 dashboard&#xff08;当前系统的实时数据面板&#xff09; 使用场景&#xff1a; 在 Arthas 中&#xff0c;dashboard 命令用于提…

旋转机械故障诊断 震动故障分析与诊断

旋转机械故障诊断 机理资料整理 电气故障&#xff0c;机械故障(不平衡&#xff0c;不对中&#xff0c;松动&#xff0c;轴承&#xff0c;共振&#xff0c;流体振动&#xff0c;皮带松动)&#xff0c;低速与高速机器故障诊断等 旋转机械故障诊断&#xff1a;机理资料整理 目录…

音视频入门基础:AAC专题(10)——FFmpeg源码中计算AAC裸流每个packet的pts、dts、pts_time、dts_time的实现

音视频入门基础&#xff1a;AAC专题系列文章&#xff1a; 音视频入门基础&#xff1a;AAC专题&#xff08;1&#xff09;——AAC官方文档下载 音视频入门基础&#xff1a;AAC专题&#xff08;2&#xff09;——使用FFmpeg命令生成AAC裸流文件 音视频入门基础&#xff1a;AAC…

移动硬盘‘需格式化‘困境:原因剖析、恢复策略与预防之道

困境直击&#xff1a;移动硬盘为何需格式化才能访问&#xff1f; 在数字化时代&#xff0c;移动硬盘作为数据存储与传输的重要工具&#xff0c;其稳定性与可靠性直接关系到用户数据的安全。然而&#xff0c;不少用户在使用过程中遭遇了“移动硬盘需要格式化才能打开”的尴尬境…

Stable Diffusion 优秀博客转载

初版论文地址&#xff1a;https://arxiv.org/pdf/2112.10752 主要流程图&#xff1a; Latent Diffusion Models&#xff08;LDMs&#xff09; DDPM是"Denoising Diffusion Probabilistic Models"的缩写&#xff0c; 去噪扩散概率模型 博客&#xff1a; 【论文阅读…

【LeetCode】146. LRU缓存

1.题目 2.思想 3.代码 3.1 代码1 下面这是一版错误的代码。错误的原因在于逻辑不正确导致最后的代码也是不正确的。 class LRUCache:def __init__(self, capacity: int):self.time 0 # 用于全局记录访问的时间self.num2time {} # 数字到时间的映射self.key2val {} # 数字…

OpenCV特征检测(8)检测图像中圆形的函数HoughCircles()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在灰度图像中使用霍夫变换查找圆形。 该函数使用霍夫变换的一种修改版本在灰度图像中查找圆形。 例子&#xff1a; #include <opencv2/imgp…

对抗攻击的详细解析:原理、方法与挑战

对抗攻击的详细解析&#xff1a;原理、方法与挑战 对抗攻击&#xff08;Adversarial Attack&#xff09;是现代机器学习模型&#xff0c;尤其是深度学习模型中的一个关键安全问题。其本质在于&#xff0c;通过对输入数据添加精微的扰动&#xff0c;人类难以察觉这些扰动&#…

计算机网络:概述 --- 体系结构

目录 一. 体系结构总览 1.1 OSI七层协议体系结构 1.2 TCP/IP四层(或五层)模型结构 二. 数据传输过程 2.1 同网段传输 2.2 跨网段传输 三. 体系结构相关概念 3.1 实体 3.2 协议 3.3 服务 这里我们专门来讲一下计算机网络中的体系结构。其实我们之前…