C++函数模板详解(结合代码)

news2024/11/13 9:31:04

目录

1. 模板概念

2. 函数模板语法

3. 函数模板注意事项

4. 函数模板案例

5. 普通函数与函数模板的区别

6. 普通函数与函数模板的调用规则

7. 模板的局限性


1. 模板概念

在C++中,模板是一种通用的程序设计工具,它允许我们处理多种数据类型而不是固定的一种。函数模板就是其中之一,它使得我们可以编写一个函数来处理不同类型的数据。

模板的特点:

  • 模板不可以直接使用,它只是一个框架,必须确定出T的类型(第3章有讲)

  • 模板的通用并不是万能的

2. 函数模板语法

C++提供两种模板机制:函数模板类模板 。为避免文章冗长,本文先介绍函数模板,下一篇文章介绍类模板。

函数模板作用:
建立一个通用函数,其函数返回值类型形参类型可以不具体制定,用一个虚拟的类型来代表。

使用语法:

template<typename T> 或者 template<class T>

template<typename T>表示声明一个模板,typename T是模板参数,
typename —— 表面其后面的符号是一种数据类型,可以用class代替。
个人习惯用template<typename T>代表函数模板,template<class T>代表类模板。
T —— 通用的数据类型,名称可以替换,通常为大写字母T。

示例:

//交换整型函数
void swapInt(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

//交换浮点型函数
void swapDouble(double& a, double& b) {
	double temp = a;
	a = b;
	b = temp;
}

//利用模板提供通用的交换函数
template<typename T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

void test01()
{
	int a = 10;
	int b = 20;
	
	//swapInt(a, b);

	//利用模板实现交换
	//1、自动类型推导
	mySwap(a, b);

	//2、显示指定类型
	mySwap<int>(a, b);

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

}

int main() {

	test01();

	system("pause");

	return 0;
}
  • 函数模板利用关键字 template

  • 使用函数模板有两种方式:1、自动类型推导    2、显示指定类型

  • 模板的目的是为了提高复用性,将类型参数化

3. 函数模板注意事项

  • 自动类型推导的限制:当使用自动类型推导时,编译器需要根据函数参数类型推导出一致的数据类型 T 才能成功实例化模板函数。如果无法推导出一致的类型,将导致编译错误。
    // 示例代码
    template<typename T>
    void myFunction(T arg1, T arg2) {
        // 函数体
    }
    
    int main() {
        myFunction(10, 20);  // 正确:推导出一致的 int 类型
        myFunction(10, 20.5);  // 错误:无法推导出一致的类型
        return 0;
    }
    
  • 模板参数的确定:在模板函数调用时,必须要确定模板参数的数据类型,否则编译器无法生成对应的函数实例。
    // 2、模板必须要确定出T的数据类型,才可以使用
    template<class T>
    void func()
    {
    	cout << "func 调用" << endl;
    }
    
    void test01()
    {
    	//func(); //错误,模板不能独立使用,必须确定出T的类型
    	func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
    }
    

fun<int>()代表对模板函数 fun 进行实例化,并指定模板参数 T 的具体类型为 int。在这种情况下,编译器会生成一个针对 T 为 int 类型的具体函数实现。其效果就好比是将模板中的 T 替换为 int,然后使用 int 类型的函数来处理相应的逻辑

4. 函数模板案例

案例描述:

  • 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序

  • 排序规则从大到小,排序算法为选择排序

  • 分别利用char数组int数组进行测试

//交换的函数模板
template<typename T>
void mySwap(T &a, T&b)
{
	T temp = a;
	a = b;
	b = temp;
}

template<typename T> // 也可以替换成class
//利用选择排序,进行对数组从大到小的排序
void mySort(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		int max = i; //最大数的下标
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i) //如果最大数的下标不是i,交换两者
		{
			mySwap(arr[max], arr[i]);
		}
	}
}
template<typename T>
void printArray(T arr[], int len) {

	for (int i = 0; i < len; i++) {
		cout << arr[i] << " ";
	}
	cout << endl;
}
void test01()
{
	//测试char数组
	char charArr[] = "bdcfeagh";
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);
}

void test02()
{
	//测试int数组
	int intArr[] = { 7, 5, 8, 1, 3, 9, 2, 4, 6 };
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

int main() {

	test01();
	test02();

	system("pause");

	return 0;
}

5. 普通函数与函数模板的区别

普通函数与函数模板区别:

  • 普通函数调用时可以发生自动类型转换隐式类型转换

  • 函数模板调用时,如果利用自动类型推导不会发生隐式类型转换

  • 如果利用显示指定类型的方式,可以发生隐式类型转换

示例:

// 普通函数和函数模板的区别

// 1、普通函数调用可以发生隐式类型转换
// 2、函数模板用 自动类型推导时,不可以发生隐式类型转换
// 3、函数模板用 显示指定类型时,  可以发生隐式类型转换

// 普通函数
int myAdd01(int a, int b)
{
	return a + b;
}

// 模板函数
template<typename T>
T myAdd02(T a, T b)
{
	return a + b;
}

void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	cout << "普通函数:int a + int b = " << myAdd01(a, b) << endl;
	// 普通函数中,隐式的将c转成了ASSIC码,c-99
	cout << "普通函数:int a + char c = " << myAdd01(a, c) << endl;
	// 自动类型推导
    // 会报错,模板函数自动类型推导时,不会发生隐式类型转换
	// cout << "模板函数:int a + char c = " << myAdd02(a, c) << endl;
	// 显示指定类型
	cout << "模板函数:int a + char c = " << myAdd02<int>(a, c) << endl;
}

int main()
{
	test01();
}

6. 普通函数与函数模板的调用规则

调用规则如下:

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数

  2. 可以通过空模板参数列表来强制调用函数模板

  3. 函数模板也可以发生重载

  4. 如果函数模板可以产生更好的匹配,优先调用函数模板

//普通函数与函数模板调用规则
void myPrint(int a, int b)
{
	cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b) 
{ 
	cout << "调用的模板" << endl;
}

template<typename T>
void myPrint(T a, T b, T c) 
{ 
	cout << "调用重载的模板" << endl; 
}

void test01()
{
	//1、如果函数模板和普通函数都可以实现,优先调用普通函数
	// 注意 如果告诉编译器  普通函数是有的,但只是声明没有实现,或者不在当前文件内实现,就会报错找不到
	int a = 10;
	int b = 20;
	myPrint(a, b); //调用普通函数

	//2、可以通过空模板参数列表来强制调用函数模板
	myPrint<>(a, b); //调用函数模板

	//3、函数模板也可以发生重载
	int c = 30;
	myPrint(a, b, c); //调用重载的函数模板

	//4、 如果函数模板可以产生更好的匹配,优先调用函数模板
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2); //调用函数模板
}

int main() 
{
	test01();
	system("pause");
	return 0;
}

既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性。

7. 模板的局限性

例如:

template<class T>
	void f(T a, T b)
	{ 
    	a = b;
    }

如果数据类型是int型,此时的赋值操作没问题。如果传入的a和b是一个数组,就无法实现了。

再例如:

template<class T>
	void f(T a, T b)
	{ 
    	if(a > b) { ... }
    }

如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行。

因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板

#include<iostream>
using namespace std;

#include <string>

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

//普通函数模板
template<class T>
bool myCompare(T& a, T& b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}

//具体化,显示具体化的原型template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template<> bool myCompare(Person &p1, Person &p2)
{
	if ( p1.m_Name  == p2.m_Name && p1.m_Age == p2.m_Age)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void test01()
{
	int a = 10;
	int b = 20;
	//内置数据类型可以直接使用通用的函数模板
	bool ret = myCompare(a, b);
	if (ret)
	{
		cout << "a == b " << endl;
	}
	else
	{
		cout << "a != b " << endl;
	}
}

void test02()
{
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	//自定义数据类型,不会调用普通的函数模板
	//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型
	bool ret = myCompare(p1, p2);
	if (ret)
	{
		cout << "p1 == p2 " << endl;
	}
	else
	{
		cout << "p1 != p2 " << endl;
	}
}

int main() 
{

	test01();

	test02();

	system("pause");

	return 0;
}

在这个特定的例子中,通用的函数模板 myCompare 用于比较两个对象是否相等,但是当对象类型是 Person 类型时,我们想要做一些特殊的比较,比如比较 Person 类的 m_Namem_Age 成员变量。为了实现这一目的,我们对通用模板进行了具体化。

具体化的语法是在 template<> 关键字后面指定特定的类型,这里是 Person,然后是模板的原型,即函数签名 bool myCompare(Person &p1, Person &p2)在函数体中,我们以特定方式实现了 Person 类型对象的比较逻辑,即比较它们的名字和年龄。

当在代码中调用 myCompare 函数,并传递 Person 类型的参数时,编译器会优先选择这个具体化版本,而不是通用的模板版本。

总结:

  • 利用具体化的模板,可以解决自定义类型的通用化

  • 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

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

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

相关文章

MVC与MVVM:两种前端架构模式对比

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

FMEA与智能机器人:提升机器人可靠性与安全性的关键

随着科技的飞速发展&#xff0c;智能机器人已经深入到我们生活的方方面面&#xff0c;从工业生产到家庭服务&#xff0c;从深海探险到太空探索&#xff0c;处处都有它们的身影。然而&#xff0c;随着应用的日益广泛&#xff0c;机器人系统的复杂性和不确定性也在增加&#xff0…

elasticsearch 8.12+kibana 8.12

准备工作&#xff1a;1.下载相关的安装包放到/usr/local/ES下面 elasticsearch下载地址:Download Elasticsearch | Elastic elasticsearch-head-master下载地址:https://github.com/mobz/elasticsearch-head/archive/master.zip node下载地址:Index of /dist/ kibana地址:Downl…

Facebook是什么?有什么功能?如何利用Facebook运营?

Facebook&#xff0c;也常被人们称为“脸书”、“脸谱”等&#xff0c;是美国的社交网络服务及社会化媒体网站&#xff0c;拥有超过20亿的月活跃用户&#xff0c;对于众多商家而言&#xff0c;Facebook以其广泛的用户基础和强大的社交影响力&#xff0c;成为了一个理想的社媒营…

(三)Qt+OpenCV调用海康工业相机SDK抓拍示例

系列文章目录 提示&#xff1a;这里是该系列文章的所有文章的目录 第一章&#xff1a; &#xff08;一&#xff09;QtOpenCV调用海康工业相机SDK示例开发 第二章&#xff1a; &#xff08;二&#xff09;Qt多线程实现海康工业相机图像实时采集 第三章&#xff1a; &#xff08;…

STM32存储左右互搏 SPI总线FATS文件读写SD/MicroSD/TF卡

STM32存储左右互搏 SPI总线FATS文件读写SD/MicroSD/TF卡 SD/MicroSD/TF卡是基于FLASH的一种常见非易失存储单元&#xff0c;由接口协议电路和FLASH构成。市面上由不同尺寸和不同容量的卡&#xff0c;手机领域用的TF卡实际就是MicroSD卡&#xff0c;尺寸比SD卡小&#xff0c;而…

iOS - Runtime-isa详解(位域、union(共用体)、位运算)

文章目录 iOS - Runtime-isa详解&#xff08;位域、union&#xff08;共用体&#xff09;、位运算&#xff09;前言1. 位域介绍1.1 思路1.2 示例 - 结构体1.3 示例 - union&#xff08;共用体&#xff09;1.3.1 说明 1.4 结构体 对比 union&#xff08;共用体&#xff09; 2. a…

【python--罗马数字转换阿拉伯数字】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Python &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 罗马数字转换阿拉伯数字相关 根据长度给出罗马序列的值生成字典根据长度给出罗马序列的值生成列表将所有…

酷开系统让用户和电视双向传递,酷开科技实现商业变现

电视在我们的日常生活中扮演着重要的角色。虽然&#xff0c;作为客厅C位的扛把子——电视的娱乐作用深入人心&#xff0c;但是&#xff0c;它的涵义和影响力却因我们每个人的具体生活环境而存在着种种差异&#xff0c;而我们的生活环境又受到我们所处的社会及文化环境的影响。 …

原生 HTML/CSS/JS 实现右键菜单和二级菜单

文章来源&#xff1a;www.huhailong.vip 站点 文章源地址&#xff1a;https://www.huhailong.vip/article/1764653112011841538 Demo效果演示地址 先看效果图 {{{width“auto” height“auto”}}} 需要注意的就是边界检测处理&#xff0c;到极端点击底部和右侧时如果不做处理会…

【网络爬虫】(2) requests模块,案例:网络图片爬取,附Python代码

1. 基本原理 1.1 requests 模块 requests 是 Python 中一个非常流行的 HTTP 客户端库&#xff0c;用于发送所有的 HTTP 请求类型。它基于 urllib&#xff0c;但比 urllib 更易用。 中文文档地址&#xff1a;Requests: 让 HTTP 服务人类 — Requests 2.18.1 文档 &#xff0…

centos 虚拟机 增加硬盘 虚拟机centos磁盘扩容

2 在centos 7 系统中挂载磁盘 2.1 查看磁盘信息 进入centos 7系统中&#xff0c;输入“# df -h”命令&#xff0c;查看磁盘信息。 这里没有写显示新增的磁盘信息。 2.2 对新加的磁盘进行分区操作 2.2.1 查看磁盘容量和分区 2.2.2 创建分区 a. 选择新增的磁盘&#xff08;这…

游戏本续航@控制中心的省电模式效果如何

文章目录 节能模式长续航模式&#x1f47a;相关工具 节能模式长续航模式&#x1f47a; 蓝天模具Control Center中的模式 根据我的试验,以及软件的提示,可以发现 Power Saving是最省电的,儿Quiet模式并不省电,它会启用独立显卡,只不过风扇的转速不像娱乐模式和性能模式那么积极而…

前端删除列表数据后页码重置逻辑

问题描述 需要调整页码的例子&#xff1a; 列表一共有10页数据&#xff0c;用户把第10页数据全部删除后&#xff0c;需要把数据重置成上一页&#xff0c;也就是第9页 不用调整页码的例子&#xff1a; 列表一共有1页数据&#xff0c;用户把本页数据全部删除后&#xff0c;页码…

代码随想录day30(2)回溯:组合(leetcode77)

题目要求&#xff1a;给定两个整数 n 和 k&#xff0c;返回 1 ... n 中所有可能的 k 个数的组合。 思路&#xff1a;首先定义两个变量&#xff0c;一个存放符合条件的单一结果&#xff0c;另一个存放符合条件结果的集合&#xff0c;for循环用来横向遍历&#xff0c;递归用来纵…

Mac电脑虚拟显示器:BetterDisplay Pro for Mac v2.0.11激活版

BetterDisplay Pro是一款由waydabber开发的Mac平台上的显示器校准软件&#xff0c;可以帮助用户调整显示器的颜色和亮度&#xff0c;以获得更加真实、清晰和舒适的视觉体验。 软件下载&#xff1a;BetterDisplay Pro for Mac v2.0.11激活版 以下是BetterDisplay Pro的主要特点&…

8.软件工程

整个章节偏向于记忆、背诵&#xff1b; 主要议题&#xff1a; 软件体系&#xff1a;3层&#xff1b; UML重点&#xff0c;重点记3要素中的关系、图&#xff1b; 1.软件体系结构 分层 优点&#xff1a;利于软件的重复利用&#xff1b; 缺点&#xff1a;以什么方式分层&#…

2024 Python3.10 系统入门+进阶(三):Python变量类型和运算符

目录 一、Python变量的定义和使用二、Python整数类型&#xff08;int&#xff09;详解三、Python小数/浮点数&#xff08;float&#xff09;类型详解四、Python复数类型(complex)详解---了解五、Python字符串详解(包含长字符串和原始字符串)5.1 处理字符串中的引号5.2 字符串的…

【Linux系统】冯诺依曼与操作系统

什么是冯诺依曼体系结构&#xff1f; 如图即为冯诺依曼大致的体系结构图&#xff0c; 我们知道这些都是由我们的计算机硬件组成 输入设备&#xff1a;键盘&#xff0c; 鼠标&#xff0c; 摄像头&#xff0c; 话筒&#xff0c; 磁盘&#xff0c; 网卡... 输出设备&#xff1a…

[HGAME 2023 week2]Designer

[HGAME 2023 week2]Designer 考点&#xff1a;XSS跨站脚本攻击&#xff0c;模板注入 代码审计 function auth(req, res, next) {const token req.headers["authorization"]if (!token) {return res.redirect("/")}try {const decoded jwt.verify(token,…