【C++】函数模板和类模版

news2024/10/5 14:00:59

目录

前言

模板参数

类型模板参数

非类型模板参数

模板的特化

函数模板的特化

类模板的特化

全特化

偏特化

模板的分离编译

模板总结 


前言

函数模板和类模板是C++模板编程中的两个核心概念,它们允许程序员编写泛型代码,这些代码可以在多种数据类型上工作,而无需为每个数据类型编写单独的实现。这提高了代码的可复用性和灵活性。所以说,模板还是很重要的。

模板参数

模板参数分为类型形参和非类型形参。

类型模板参数

出现在模板参数列表中,跟在class或者typename之后的参数类型名称。

//T可以是int、double等任何类型
template <class T>
T Add(T& a, T& b)
{
	return a + b;
}

非类型模板参数

非类型模板参数,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

非类型模板参数不一定是整形常量,也可以是double等类型。需要注意的是,非类型模板参数必须在编译期就能确定结果。

//N就是非类型模板参数
template <class T, size_t N = 10>
class array
{
public:
	T& operator[](size_t index)
	{
		return _array[index];
	}
	const T& operator[](size_t index) const
	{
		return _array[index];
	}
	size_t size() const
	{
		return _size;
	}
	bool empty() const
	{
		return _size == 0;
	}
private:
	T _array[N];
	size_t _size;
};

模板的特化

特化的概念:特化是一种技术,当模板参数为某些特殊类型时,开发者可以为其定义特定的实现。这种技术允许开发者在需要时覆盖模板的默认行为,为特定类型或类型组合提供优化或特定的功能。

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型可能会产生错误的结果,需要特殊处理。例如,实现一个专门用来进行小于比较的函数模板。


template <class T>
bool less(T left, T right)
{
	return left < right;
}

int main()
{
	cout << less(1, 2) << endl;//可以正确比较

	Date d1(2024, 6, 8);
	Date d2(2024, 6, 9);

	cout << less(d1, d2);//可以正确比较

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << less(p1, p2) << endl;//比较结果错误

	return 0;
}

可以看到,对于最后的特殊场景,比较结果是错误的。原因在于,比较的是p1和p2指针的地址,而不是比较它们指向的内容。

此时,就需要对函数模板进行特化了。模板特化,就是在原模板的基础上,针对特殊类型进行特殊化的实现方式。模板特化分为类模板特化和函数模板特化。 

函数模板的特化

步骤:

1)必须要先有一个基础的函数模板

2)关键字template后面接一对空的尖括号<>

3)函数名后跟一对尖括号<>,尖括号中指定需要特化的类型


template <class T>
bool less(T left, T right)
{
	return left < right;
}

//函数模板的特化
template <>
bool less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}

int main()
{
	cout << less(1, 2) << endl;//可以正确比较

	Date d1(2024, 6, 8);
	Date d2(2024, 6, 9);

	cout << less(d1, d2);//可以正确比较

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << less(p1, p2) << endl;//比较结果错误

	return 0;
}

但是,一般情况下,如果函数模板遇到不能处理或者处理有误的类型,为了实现简单,通常都是将该函数直接给出。

bool less(Date* left, Date* right)
{
	return *left < *right;
}

 这样实现简单明了,代码的可读性高容易书写,因此函数模板不建议特化。

类模板的特化

类模板的特化分为两种,全特化和偏特化(局部特化\半特化)。

全特化

全特化就是将模板参数列表中的所有参数都确定化。

template <class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

//全特化
template<>
class Data<int, char>
{
public:
	Data() { cout << "Data<int, char>" << endl; }
private:
	int _d1;
	char _d2;
};

偏特化

  • 偏特化是指为模板类或模板函数提供一份基于原始模板定义但针对部分模板参数进行条件限制或更具体实现的定义。
  • 将模板参数列表中的一部分参数特化
  • 参数更进一步的限制,例如限定参数的类型

将模板参数列表中的一部分参数特化

template <class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

//将模板参数列表中的一部分参数特化
template <class T1>
class Data<T1, int>
{
public:
	Data() { cout << "Data<T1, int>" << endl; }
private:
	T1 _d1;
	int _d2;
};

参数类型更进一步的限制

1)两个参数偏特化为指针类型

template <class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

//进一步限制参数
template <typename T1, typename T2>
class Data<T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

 如果都是指针类型,就会走上面的这个偏特化。

2)两个参数偏特化为引用类型

template <class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

template <typename T1, typename T2>
class Data<T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		:_d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}
private:
	const T1& _d1;
	const T2& _d2;
};

 如果都是引用类型,那么将走这个偏特化。

模板的分离编译

分离编译的概念:

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有的目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

模板在C++中并不直接支持传统意义上的分离编译,即声明和定义放在不同的文件中。这是因为模板的实例化是在编译时进行的,编译器需要看到模板的定义才能对模板进行实例化。

请看一下的场景:.h放声明,一个.cpp文件放定义,然后在另一个.cpp文件中调用。

//a.h
template <class T>
T Add(const T& left, const T& right);

//a.cpp
template <class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

//main.cpp
#include "a.h"
int main()
{
	Add(1, 2);
	return 0;
}

这就会导致链接错误。原因如下:

首先,每个源文件(加上其包含的头文件)通常被视为独立的编译单元,编译器会单独编译每个编译单元,这些编译单元在编译期间不会进行交互。因为main.cpp包含了头文件,所以头文件会插入该源文件中并在编译时被考虑,但是在编译main.cpp的过程中,函数模板Add只有声明,没有定义,因此它没有被实例化。在编译a.cpp的过程中,编译器没有看到对Add函数的实例化,因此不会生成具体的加法函数。在main.cpp中调用Add<int>,编译器在链接时才会找其地址,但是这个函数并没有实例化生成具体的代码,因此链接错误。

解决方法:

1)将声明和定义放到一个.hpp或.h文件中。

2)模板定义的位置显示实例化。

显示实例化:

//a.h
template <class T>
T Add(const T& left, const T& right);

//a.cpp
template <class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

//显示实例化
template
int Add(const int& left, const int& right);


//main.cpp
#include "a.h"
int main()
{
	Add(1, 2);
	return 0;
}

模板总结 

【优点】

1)复用了代码,更快的迭代开发,C++标准模板库STL因此产生。

2)增强了代码的灵活性。

【缺点】

1)模板会导致代码膨胀问题,也会导致编译时间变长。

2)出现模板编译错误时,错误信息非常凌乱,不易定位错误。

【模板导致编译时间变长的几个原因】

1)类型推导

模板在编译阶段需要进行类型推导。当编译器遇到模板是,它需要根据模板参数的类型生成相应的代码。这个过程需要编译器进行额外的分析和计算,因此消耗更多的编译时间。

2)代码膨胀

模板的使用可能导致代码膨胀。由于模板在编译时回生成实际的代码,这可能导致生成的代码量远大于原始代码,大量的代码需要编译器进行解析和处理,从而导致编译时间变长。


完~

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

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

相关文章

嵌入式单片机中项目在线仿真工具分享

前段时间,无意间发现了一个不错的在线仿真工具(Wokwi),支持多种平台,支持市面上主流的开发板,比如:STM32、ESP32、Arduino、树莓派等。 还支持常见的传感器、显示器件(LCD、LED屏幕)等,还可以播放音乐、联网、逻辑分析仪等,关键还提供了很多实际项目的案例。 这款工…

对称加密系统解析

目录​​​​​​​ 1.概述 2. 对称密码类型 3. 对称加密优缺点 4. 对称加密算法 4.1 DES 4.2 3DES 4.3 AES ​​​​​​4.4 SM1 4.5 SM4 1.概述 对称加密&#xff0c;是指在加密和解密时使用同一秘钥的方式。秘钥的传送和保存的保护非常重要&#xff0c;务必不要让秘…

【React】json-server

1.安装到开发环境 npm install json-server -D2.在根目录下下&#xff0c;新建db.json文件 {"list": [{"rpid": 3,"user": {"uid": "13258165","avatar": "http://toutiao.itheima.net/resources/images/9…

显示子系统,显示子前后端,linuxfb,wayland

显示前端 显示前端通常指的是在图形系统中负责生成图形数据的部分或组件。它负责接收来自应用程序或图形引擎的图形数据&#xff0c;并将其转换成适合显示的格式&#xff0c;以便发送到显示后端进行处理和输出。 显示前端的功能通常包括以下几个方面&#xff1a; 图形数据生…

自定义类型:枚举(enum)+联合体(union)

枚举联合体 一.枚举1.枚举类型的声明2.枚举类型的优点3.枚举类型的使用 二.联合体1.联合体类型的声明2.联合体的特点3.相同成员的结构体和联合体对比4.联合体大小的计算5.联合体的练习&#xff08;判断大小端&#xff09;6.联合体节省空间例题 一.枚举 1.枚举类型的声明 枚举…

《软件定义安全》之四:什么是软件定义安全

第4章 什么是软件定义安全 1.软件定义安全的含义 1.1 软件定义安全的提出 虚拟化、云计算、软件定义架构的出现&#xff0c;对安全体系提出了新的挑战。如果要跟上网络演进的步伐和业务快速创新的速度&#xff0c;安全体系应该朝以下方向演变。 &#x1d7ed; 安全机制软件…

springcloud gateway扩展支持多版本灰度

改造要求 需要在原有的调度策略中通过客户端header中的version进行1个服务多实例下进行二次分组&#xff0c;让指定的version在指定的版本实例下进行轮训调度。 需要改造的点 1.业务服务在发布到naocs中的元数据需要指定版本号 2.网关的调度策略中需要增加版本的区分 3.无…

LeetCode | 1.两数之和

这道题&#xff0c;很容易想到的是暴力解&#xff0c;直接一个双重循环&#xff0c;对于数组中的每一个数&#xff0c;都去遍历其他数字&#xff0c;看能不能找到数字等于target-nums[i]的数字&#xff0c;时间复杂度为 O ( n 2 ) O(n^2) O(n2) 但是通过其他题目&#xff0c;我…

【算法专题--链表】环形链表--高频面试题(图文详解,小白一看就会!!)

目录 一、前言 二、什么是环形链表&#xff1f; &#x1f95d; 环形链表的概念详解 &#x1f347; 环形链表的特点 &#x1f34d;环形链表的延申问题 三、高频面试题 ✨环形链表 ✨环形链表II ✨环形链表的环长 四、总结 五、共勉 一、前言 环形链表&#xff0c;可以说是…

VSCode超过390万下载的请求插件

Thunder Client 是一款在 VSCode&#xff08;Visual Studio Code&#xff09;中非常受欢迎的 REST API 客户端插件&#xff0c;由Ranga Vadhineni开发&#xff0c;现在已经有超过390万的下载量。它允许开发者直接在编辑器内发送 HTTP 请求&#xff0c;查看响应。Thunder Client…

力扣hot100:739. 每日温度/54. 螺旋矩阵

文章目录 一、 739. 每日温度二、54. 螺旋矩阵1、模拟螺旋矩阵的路径2、按层模拟 一、 739. 每日温度 LeetCode&#xff1a;739. 每日温度 经典单调栈问题&#xff0c;求下一个更大的数。 使用单调递减栈&#xff0c;一个元素A出栈&#xff0c;当且仅当它第一次出现比它更大…

bitset用法

参考:https://blog.csdn.net/weixin_45697774/article/details/105563993 题目:https://leetcode.cn/problems/maximum-total-reward-using-operations-ii/description/ class Solution { public:int maxTotalReward(vector<int>& rewardValues) {bitset<10000…

46.django - 多语言配置

1.Django 多语言基础知识 多语言站点可以让不同语言的用户更好地使用和理解网站内容&#xff0c;提升用户体验和覆盖范围。为了实现多语言功能&#xff0c;我们将使用Django内置的国际化和本地化支持。我收集了一些知识点整理在这一部分&#xff0c;感兴趣的可以看看。直接跳过…

单臂路由的配置(思科、华为)

#交换设备 不同vlan属于不同广播域&#xff0c;不能互相通信&#xff0c;他们配置的是不同网段的IP地址&#xff0c;针对不同网段的IP地址进行通信&#xff0c;就需要用到路由技术 实现不同vlan之间的通信技术有两种 单臂路由三层交换 单臂路由 一、思科设备的单臂路由配…

【python】OpenCV—Histogram Matching(9.2)

学习来自OpenCV基础&#xff08;17&#xff09;基于OpenCV、scikit-image和Python的直方图匹配 文章目录 直方图匹配介绍scikit-image 中的直方图匹配小试牛刀风格迁移 直方图匹配介绍 直方图匹配&#xff08;Histogram Matching&#xff09;是一种图像处理技术&#xff0c;旨…

Web 自动化测试(基于Pytest极简)

Pytest 初体验 在使用 Python 进行 Web UI 自动化测试时&#xff0c;我们除了使用 unittest 单元测试框架&#xff0c;还可以使用 pytest&#xff0c;本节实验就给大家简单的介绍一下 pytest。 环境配置 本系列实验我们借助 VS Code 工具编写代码&#xff0c;使用的 Python …

[Llama3] ReAct Prompt 测试实验

ReAct 是一种 LLM 提示和结果处理方法&#xff0c;结合了推理、行动计划和知识源整合&#xff0c;使 LLM 超越其语言模型&#xff0c;并在预测中使用来自现实世界的信息。 ReAct 是推理和行动的结合。 介绍 ReAct 的论文表明它比思维链提示更好。与后者不同的是&#xff0c;Re…

【数据结构(邓俊辉)学习笔记】图06——最小支撑树

文章目录 0. 概述1. 支撑树2. 最小支撑树3. 歧义性4. 蛮力算法5. Prim算法5.1 割与极短跨越边5.2 贪心迭代5.3 实例5.4 实现5.5 复杂度 0. 概述 学习下最小支撑树和prim算法。 1. 支撑树 最小的连通图是树。 连通图G的某一无环连通子图T若覆盖G中所有的顶点&#xff0c;则称…

Layui弹框中设置输入框自动获取焦点无效/Layui设置Input框自动获取焦点无效,怎么办?

1、问题概述? 有时候为了用户体验,期望当弹框打开的时候,指定的输入框能自动的获取焦点,用户就可以直接输入了。提升了用户体验。但有时候设置的时候没有效果。 2、正常的设置自动获取焦点方式 【input框设置方式】 使用关键字autofocus <input type="text&quo…

Linux - 逻辑卷的创建和管理

1.逻辑卷LVM的创建 1.1 创建步骤 ①添加硬盘或者创建分区 ②创建物理卷 pvcreate ③创建卷组 vgcreate ④创建逻辑卷 lvcreate ⑤创建文件系统 mkfs.xfs/ect4/... ⑥创建挂…