模板(进阶)

文章目录

  • 类型模板参数与非类型模板参数
  • 模板的特化
  • 模版不能声明和定义分离的原因
  • 模板总结

类型模板参数与非类型模板参数

虽然这东西好像没什么记录的必要……但老师对此的描述太好了,不记下来的话难受

类型模板参数:

//C语言(数据结构)期间我们模拟实现时,为了在修改存储数据类型时能更加的便捷,我们使用了typedef:
typedef int DataType;
//而这么写不能让我们在一个代码中存储不同类型的数据,而 C++中,类 型 模 板 解决了这一问题:
template<class T>
class A{……};
A<int> a1; A<double> a2;

非类型模板参数:

//对于以下代码:
#define N 20;
template<class T>
class Array{
private:
	T _a[N];
}
//目前为止,我们做不到让不同对象中的 N 不同
//非类型模板参数则能解决这个问题:
template<class T, size_t N> //● 规定这是 整 形 常 量(所以 int N 也是没有问题的)
class Array{
private:
	T _a[N];
}
//…………………………
Array<int,10> a1;
Array<double,20> a2;

模板的特化

对某些类型进行特殊化处理

函数模板的特化

//实现一个日期类 Date(略)
template<class>
bool Less(T left, T right){
	return left < right;
}
cout << Less(1,2) << endl;//可以比较,结果正确
Date d1(2022,7,7);
Date d1(2022,7,8);
cout << Less(d1,d2) << endl;//可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1,p2) << endl;//可以比较,结果错误
//为了解决这种特殊情况,我们可以使用模板特化(此处为函数模板特化):
template<>
bool Less<Date*>(Date* left, Date* right){
	return *left < *right;
}

实际上函数模板的特化意义不大,上面用重载一样能解决问题

类模板的特化:
比如此前在 “栈和队列” 中实现的优先级队列,我们用仿函数去控制大小堆,当存储对象为日期类的指针时,我们需要再写一个仿函数 PDateGreater 并在创建对象时主动传入。
但如果我们总要储存这样的对象,每次都主动传入 PDateGreater 未免有些麻烦,于是就可以用类模板解决这一问题:

//类中原本的仿函数
	template<class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
//类模版
	template<>
		struct less<Date*>
	{
		bool operator()(const Date* x, const Date* y)
		{
			return *x < *y;
		}
	};

偏特化 示例1:

//●上面类模版的特化用的是是全特化,也可以用偏特化:
		template<>
		struct less<T*>
	{
		bool operator()(const T* x, const T* y)
		{
			return *x < *y;
		}
	};

偏特化 示例2:

class Data
{
public:
	Data() { cout << "Data" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
template <class T1>
class Data<T1, int>
{
public:
	Data() { cout << "Data" << endl; }
private:
	T1 _d1;
	int _d2;
};
int main() {
	Data<int, double> d1;
	Data<int, int> d2;
	return 0;
}

模板的特化都是建立在有源模板的基础之上的

模版不能声明和定义分离的原因

准确的说是不能声明和定义不能处于不同的项目文件中

比如这样两个函数:

//Calculate.h
template<class T>
T Add(const T& x, const T& y);
int func(int x, int y);
//Calculate.c
#include"Calculate.h"
template<class T>
T Add(const T& x, const T& y) {
	return x + y;
}
int func(int x, int y) {
	return x + y;
}
//test.c
#include"Calculate.h"
#include
using namespace std;
int main() {
	cout << func(1, 2) << endl;
	cout << Add(1, 2) << endl;
}

语法上不会报错(能正常编译),但最后链接时出现问题
原因:预处理过后(经头文件包含,剩下 Calculate.i 和 test.i),正常编译、汇编(我想不用解释),在编译期间,test.i 中只有声明,因而形成如下所示的指令:

func(?) //?表示相应的函数的地址(只根据声明知道你可能在其他文件中定义了这个函数,但不知道具体在哪里)
Add(?)

链接时,func 函数可以实例化,能填充相应的地址,但 Add 不行(不知道T到底是什么,也就没办法实例化)

解决方式:

  1. 在一个项目文件中声明和定义分离(也就是我们常做的把Calculate.c的代码放到Calculate.h里去)
    能这么做的原因:预处理过后test.i文件:
template<class T>
T Add(const T& x, const T& y);
int func(int x, int y);
template<class T>
T Add(const T& x, const T& y) {
	return x + y;
}
int func(int x, int y) {
	return x + y;
}
int main() {
	cout << func(1, 2) << endl;
	cout << Add(1, 2) << endl;
}

函数的定义就在本文件中,编译期间直接实例化并将地址填入,链接?不需要链接:

func(0x~~) 
Add(0x~~)

有些文件会以.hpp为后缀名暗示这个文件中声明定义分离:
eg. boost 库:
模板(进阶)_第1张图片

  1. 显示实例化
//在 Calculate.c后面加上诸如:
template
double Add<double>(const double& x, const double& y); 
template
int Add<int>(const int& x, const int& y); 

显然是一种很拉的方法

模板总结

● 优点:

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性

● 缺陷:

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

你可能感兴趣的:(c++)