【C++ | 泛型编程】C++函数模板详解(定义、使用、特化、重载)

news2024/11/16 3:36:19

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍C++泛型编程的函数模板 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
    • ✨1.1 函数模板是什么?为什么需要函数模板?
  • 🎄二、函数模板的定义
  • 🎄三、函数模板的使用
  • 🎄四、函数模板的局限性与特化
  • 🎄五、函数模板的重载
  • 🎄六、总结



在这里插入图片描述

🎄一、概述

介绍函数模板之前,先了解一下泛型编程。

泛型编程是一种编程范式,它强调在编写代码时 尽可能地使算法和数据结构独立于所处理的数据类型

在泛型编程中,使用泛型(通常通过模板在 C++ 等语言中实现)来创建可以处理多种不同类型数据的通用函数、类和数据结构,而无需为每种具体的数据类型单独编写重复的代码。其主要优点包括:

  • 提高代码的复用性:一次编写的泛型代码可以用于多种不同的数据类型,减少了重复开发。
  • 增强代码的可读性和可维护性:因为通用的逻辑被集中在一个地方,而不是分散在针对不同类型的多个实现中。
  • 提高程序的灵活性:可以轻松地适应新的数据类型,而无需对现有代码进行大量修改。

✨1.1 函数模板是什么?为什么需要函数模板?

模板是 C++中泛型编程的基础。函数模板可以说是一个创建函数的蓝图或公式,在代码编译期间按照调用方式转换成特定类型的函数。

🌰先看个例子,我们写一个函数比较两个值,如果有多个类型,我们可能需要重载,像下面代码一样:

int compare(const string &v1, const string &v2)
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

int compare(const double &v1, const double &v2)
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

这两个函数几乎是相同的,唯一的差异是参数的类型,函数体则完全一样。对于这种只有类型差异的函数,我们就可以使用函数模板来编写代码,让编译器根据调用时使用的参数类型去帮我们转换成对应的函数。这样可以避免写相同的代码。


在这里插入图片描述

🎄二、函数模板的定义

函数模板的定义看起来与普通函数相似,但使用了模板参数。模板参数通常放在尖括号 <> 内。以下是上个小节compare函数的函数模板:

template<typename T>
int compare(const T &v1, const T &v2)
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

我们看看函数模板是怎样定义的:

  • 1、使用关键字 template 表示定义一个模板;
  • 2、template 后面是使用尖括号<>模板参数列表,这是用逗号分隔的零个或多个模板参数;
  • 3、模板参数列表里可以使用关键字typenameclass来传递一个类型,一般使用typename。之所以会有class来传递类型,是因为在标准 C++98 添加关键字 typename 之前,C++使用关键字 class 来创建模板:
    template<typename T, class T2>
    
  • 4、在函数编写时,需要使用类型的地方,使用模板参数里面的对应类型T来替换。
  • 5、虽然类型名可以自己定义,当常见的做法,是使用T来作为类型名。
  • 6、模板参数有两种,模板类型参数(参数前使用typenameclass)、非类型模板参数(使用特定类型名指定)
    template<typename T, class T2, int N>
    

注意:模板也可以有声明,但需要将 模板参数列表 带上,并且模板的声明和定义必须在同一文件中。更常见的情形是,将模板放在头文件中,并在需要使用模板的文件中包含头文件

template<typename T> int compare(const T &v1, const T &v2);  // 模板声明
...
template<typename T>	// 模板定义
int compare(const T &v1, const T &v2)
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

在这里插入图片描述

🎄三、函数模板的使用

使用函数模板时,编译器会根据传递的实际参数类型来为我们 实例化 (instantiate) 一个特定版本的函数。C++中有允许使用下面两种方式来使用函数模板:①自动推导参数类型;②指定模板参数类型。

  • 自动推导参数类型
    自动推导参数类型,是指在使用函数模板时,直接传入实参,让编译器根据传递的实际参数类型来自动生成相应的函数实例。例如下面代码:
    int x=5, y=6;
    compare(x,y);	// 编译过程中,生成 int compare(const int &v1, const int &v2)
    
    double f=1.23, d=4.56;
    compare(f,d);	// 编译过程中,生成 int compare(const double &v1, const double &v2)
    
  • 指定模板参数类型
    一般情况下,在调用函数模板时,编译器通常能够根据传递的参数自动推导模板参数的类型。但我们也可以显式指定模板参数类型,例如:
    int x=5, y=6;
    compare<int>(x,y);	// 编译过程中,生成 int compare(const int &v1, const int &v2)
    
    double f=1.23, d=4.56;
    compare<double>(f,d);	// 编译过程中,生成 int compare(const double &v1, const double &v2)
    

再次强调一下,函数模板并不是函数,它会在编译期间根据调用方式实例化一个函数出来,只是这个函数我们是看不到的,那怎么证明它的存在呢?你可以使用一个函数指针去指向一个函数模板的实例,然后打印出其函数地址,如下代码,最终打印出来的PFun、PFun1将会是同一个地址:

int (*PFun)(const int&, const int&) = compare<int>;
int (*PFun1)(const int&, const int&) = compare<int>;
cout << "PFun=" << (unsigned long)*PFun << ", PFun1=" << (unsigned long)*PFun1 << endl;

🌰完整例子:

// g++ 25_fun_template.cpp
#include <iostream>

using namespace std;

template<typename T> int compare(const T &v1, const T &v2);  // 模板声明

int main()
{
	int x=5, y=6;
	int ret = compare(x,y);	// 编译过程中,生成 int compare(const int &v1, const int &v2)
	cout << "x=" << x << ", y=" << y << ", compare ret is " << ret << endl;

	double f=1.23, d=4.56;
	ret=compare(f,d);	// 编译过程中,生成 int compare(const double &v1, const double &v2)
	cout << "f=" << f << ", d=" << d << ", compare ret is " << ret << endl;
	
	compare<int>(x,y);	// 编译过程中,生成 int compare(const int &v1, const int &v2)
	compare<double>(f,d);// 编译过程中,生成 int com1pare(const double &v1, const double &v2)

	int (*PFun)(const int&, const int&) = compare<int>;	// 函数指针 PFun 指向 compare<int> 函数
	int (*PFun1)(const int&, const int&) = compare<int>;// 函数指针 PFun1 指向 compare<int> 函数
	cout << "PFun=" << (unsigned long)*PFun << ", PFun1=" << (unsigned long)*PFun1 << endl;
	
	return 0;
}

template<typename T>	// 模板定义
int compare(const T &v1, const T &v2)
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

运行结果如下:
在这里插入图片描述


在这里插入图片描述

🎄四、函数模板的局限性与特化

虽然函数模板有很多优点,但也存在一些局限。模板实例化时,传入的类型必须支持函数模板函数体内的所有操作

例如,上面的 compare 函数模板,它的函数体内使用了 < 操作来判断两个对象的大小,所以就要求实例化时,传入的对象的类型必须支持 < 操作,否则就会报错。假设我们传入一个结构体对象来实例化这个函数模板就会编译不通过。这个就是函数模板的局限性。

那怎样解决这个问题呢?有2个方法,一是给传入的结构体重载<运算符;二是使用函数模板的特化,定义一个支持这种类型的函数模板。

什么时候需要使用函数特化?如果某个函数模板在处理某种类型时,其原有的函数体需要改变(存在不支持该类型的操作),就可以考虑使用函数模板的特化(specialization)

函数模板特化的步骤:
1、函数原型前加template <>
2、函数名后加<Type>,Type为要特化的类型。这一步可以省略。

template<> int compare<stType>(const stType &v1, const stType &v2) 	// 模板特化
{
	if (v1.id < v2.id) return -1;
	if (v2.id < v1.id) return 1;
	return 0;
}

上面代码是 compare 函数模板关于stType类型的特化。意思是告诉编译器不要使用 compare 函数模板来为 stType 类型生成函数定义,而是使用专用的特化模板来生成。

编译器在选择原型时,非模板版本优先于模板特化版本函数模板版本, 而模板特化版本优先于使用模板生成的版本。

完整例子:

// g++ 25_fun_template1.cpp
#include <iostream>

using namespace std;

struct stType
{
	int id;
	char name[20];
};

template<typename T> int compare(const T &v1, const T &v2);  // 模板声明
template<> int compare<stType>(const stType &v1, const stType &v2);
int compare(const stType &v1, const stType &v2);

int main()
{
	int x=5, y=6;
	int ret = compare(x,y);	// 编译过程中,生成 int compare(const int &v1, const int &v2)
	cout << "x=" << x << ", y=" << y << ", compare ret is " << ret << endl;
	
	stType st1, st2;
	ret=compare(st1,st2);
	return 0;
}

template<typename T>	// 模板定义
int compare(const T &v1, const T &v2)
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

template<> int compare<stType>(const stType &v1, const stType &v2) 	// 模板特化
{
	cout << "compare<stType>" << endl;
	if (v1.id < v2.id) return -1;
	if (v2.id < v1.id) return 1;
	return 0;
}

int compare(const stType &v1, const stType &v2)
{
	cout << "compare" << endl;
	if (v1.id < v2.id) return -1;
	if (v2.id < v1.id) return 1;
	return 0;
}

在这里插入图片描述

🎄五、函数模板的重载

使用函数模板时会发现,并非所有的类型都使用相同的算法。为满足这种需求,可以像重载常规函数定义那样重载模板定义。重载函数模板也是要求 参数数目、参数类型、参数的排列顺序 不同。

下面这些都会构成函数模板的重载:

template<typename T>	// 模板定义
int compare(const T &v1, const T &v2)
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

template<typename T>
int compare(const T &v1, const T &v2, int n)// 参数个数不一样,构成重载
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

template<typename T, typename T1>
int compare(const T &v1, const T1 &v2)// 参数类型不一样,构成重载
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

template<typename T, typename T1>
int compare(const T1 &v1, const T &v2)// 参数顺序不一样,构成重载
{
	if (v1 < v2) return -1;
	if (v2 < v1) return 1;
	return 0;
}

在这里插入图片描述

🎄六、总结

👉本文介绍了C++函数模板,函数模板定义、函数模板的使用、函数模板的特化、函数模板的重载。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

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

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

相关文章

Stable Diffusion绘画 | 图生图-局部重绘(三)

重绘区域 整张图片 指重绘的分辨率是应用在整个画面上的&#xff0c;会整个画面综合参与运算。 选择一片空白区域&#xff0c;提示词添加 a book&#xff1a; 操作生成图片&#xff1a; SD会觉得整个画面中&#xff0c;蒙版区域出现一本书不太合理&#xff0c;索性生成一张木…

【多模态】43、INF-LLAVA | 使用双视角裁剪和双视角增强模块来提升模型对大分辨率图片的处理能力

论文&#xff1a;INF-LLaVA: Dual-perspective Perception for High-Resolution Multimodal 代码&#xff1a;https://github.com/WeihuangLin/INF-LLaVA 出处&#xff1a;厦大 时间&#xff1a;2024.07.23 贡献&#xff1a; 提出了双视角裁剪模块&#xff08;Dual-perspe…

【后续 断点续传】前端大文件分片下载解决方案,没用你来砍我

前言 之前已经出过 大文件分片下载 的教程&#xff0c;期间也收到很多小伙伴的疑问说是功能上有点问题&#xff0c;也抽时间将一些大的问题修改了&#xff0c;验证了很多次&#xff0c;应该不会有什么问题了&#xff1b;在下载方案中涉及到断点续传部分的没有细讲&#xff0c;…

【MySQL】索引和事务

秋招 秋招中最经典&#xff0c;最高频的面试题 文章目录 索引 index操作索引的 SQL查看索引创建索引删除索引 事务操作四个核心特性 索引 index 在数据库中建立一个特殊的“目录“&#xff08;一系列特定的数据结构&#xff09;&#xff0c;为了加快查询速度 select 查询都是遍…

Go项目依赖:Tidy命令与离线依赖获取

引言 Go语言以其简洁性和高效性在开发者中广受欢迎&#xff0c;而Go的模块系统则为依赖管理提供了极大的便利。本文将介绍如何使用go mod tidy命令来整理项目的依赖关系&#xff0c;以及在无法在线获取依赖的情况下&#xff0c;如何通过离线方式获取并使用依赖。 使用go mod …

数论基础知识(下)

目录 欧拉函数 n的分解质因数求欧拉函数 试除法求欧拉函数值 积性函数 筛法 朴素筛 埃氏筛 欧拉筛 (线性筛) 线性筛欧拉函数 快速幂 同余 欧拉定理 费马小定理 乘法逆元 欧拉函数 互质 : ∀ a , b ∈ N &#xff0c;若 gcd ( a , b ) 1 &#xff0c;则 a…

Java零基础之多线程篇:线程控制

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

RISC-V竞赛|第二届 RISC-V 软件移植及优化锦标赛报名正式开始!

目录 赛事背景 赛道方向 适配夺旗赛 优化竞速赛 比赛赛题&#xff08;总奖金池8万元&#xff01;&#xff09; &#x1f525;竞速赛 - OceanBase 移植与优化 比赛赛程&#xff08;暂定&#xff09; 赛事说明 「赛事背景」 为了推动 RISC-V 软件生态更快地发展&#xff0…

健身动作AI识别,仰卧起坐计数(含UI界面)

用Python和Mediapipe打造&#xff0c;让你的运动效果一目了然&#xff01; 【技术揭秘】 利用Mediapipe的人体姿态估计&#xff0c;实时捕捉关键点&#xff0c;精确识别动作。 每一帧的关键点坐标和角度都被详细记录&#xff0c;为动作分析提供数据支持。 支持自定义动作训练&a…

Jave-this关键字

目录 1.this关键字 先看一段代码&#xff0c;并分析问题 什么是this 案例演示 2.this的注意事项和使用细节 1.this关键字 先看一段代码&#xff0c;并分析问题 什么是this this就 代表的是当前对象。 this可以大概理解成“我的” &#xff0c;比如我的书包&#xff0c;这…

基于Java中的SSM框架实现大创项目申报管理系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现大创项目申报管理系统演示 摘要 经济的高速发展进一步推动了种类繁多的项目落地&#xff0c;传统的项目在申报过程中主要通过纸质文件的方式实现申报信息的传递&#xff0c;同时不同的项目内容所对应的申报资料模板也有所差异&#xff0c;人工整理的提…

没有显卡,怎么玩AI绘图?

或许很多人跟我一样&#xff0c;没有显卡&#xff0c;但又很想玩AI绘图&#xff0c;但本地绘图怕是无缘了&#xff0c;只能借助云GPU的方式了。 今天跟大家分享一下一个简单目前可白嫖无门槛的方法实现无显卡也能玩AI绘图。 方案就是ComfyUIBizyAir云节点。 ComfyUI介绍 来…

【软考】虚拟存储器

目录 1. 说明2. 定义3. 工作原理4. 管理方式5. 优点6. 例题6.1 例题1 1. 说明 1.在概念上&#xff0c;可以将主存存储器看作一个由若干个字节构成的存储空间&#xff0c;每个字节(称为一个存储单元)有一个地址编号&#xff0c;主存单元的该地址称为物理地址(Physical Address)…

数值分析——三次样条插值

系列文章目录 数值分析——拉格朗日插值 数值分析——牛顿插值多项式 数值分析——埃尔米特&#xff08;Hermit&#xff09;插值 数值分析——分段低次插值 文章目录 系列文章目录前言一、理论推导1.三次样条函数2.三次样条插值函数的求解条件3.三次样条插值函数的建立 二、MA…

简单反射型XSS的复现

xss反射型攻击&#xff1a; 1.最简单的漏洞复现&#xff1a; 这里我们有一个最简单的网页&#xff1a;由于地址不存在&#xff0c;所以图片加载不出来。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta…

FP8量化

https://arxiv.org/html/2402.16363v5 LLama3.1用了FP8量化&#xff1a; FP8也可以用scaling factor来扩大表示范围&#xff0c;对吧&#xff1f;

开源在线剪切板 PrivateBin 安装和使用教程

我们经常需要在网上快速分享一些文本内容&#xff0c;比如代码片段、临时笔记或者敏感信息。传统的在线剪贴板服务虽然使用方便&#xff0c;但往往缺乏足够的隐私保护。 那么&#xff0c;有没有一种既方便又安全的在线文本分享方式呢&#xff1f;今天我要向大家推荐一个优秀的…

常见的图像融合方法

这里我们将介绍一些常用的图像融合方式&#xff0c;并不涉及到诸如CutMix、MixUp、TokenMix、Mosaic、Copy-Paste等图像增强方法。 首先是读取图像&#xff0c;我们这边采用了PIL库进行&#xff0c;那么读进来就应该是一个Image对象。下面介绍Image对象与array的转换方式。 f…

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测

土地利用/土地覆盖数据是生态、环境和气象等领域众多模型的重要输入参数之一。基于遥感影像解译&#xff0c;可获取历史或当前任何一个区域的土地利用/土地覆盖数据&#xff0c;用于评估区域的生态环境变化、评价重大生态工程建设成效等。借助CLUE模型&#xff0c;实现对未来土…

AI9-文本识别

本章主要介绍文本识别算法的理论知识,包括背景介绍、算法分类和部分经典论文思路。 通过本章的学习,你可以掌握: 1. 文本识别的目标 2. 文本识别算法的分类 3. 各类算法的典型思想 1 背景介绍 文本识别是OCR(Optical Character Recognition)的一个子任务,其任务为识别一个…