C++初阶学习————模板使用介绍(2)

非类型模板参数

模板参数分为:类型形参与非类型形参
类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称

template<class T>

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

template<size_t N>

例如想指定一个静态数组的长度

#define N 100
template<class T>
class array
{

private:
	T _arr[N];
};

上述定义数组长度还可以用宏、枚举等等,但修改长度比较麻烦;类似于在C语言中typedef可以给类型起别名,但是想换类型还得需要在typedef时修改所以C++中使用了模板,同样的想改变常量大小可以增加一个非类型模板参数

//#define N 100
template<class T,size_t N = 100>
class array
{

private:
	T _arr[N];
};
C++11中 array是一个静态数组,相比直接定义数组的优势是会检查越界

注意:非类型模板参数必须是整型相关的类型(C++20之前是,C++20以后不清楚,佬们有知道的可以告诉我一下)

模板的特化

函数模板特化

假设有下面这样一段代码,想比较日期类的大小

template<class T>
bool compare(const T& x, const T& y)
{
	return x < y;
}

struct Data
{
	Data(int year,int month ,int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

	int _year;
	int _month;
	int _day;
};
void test7()
{
	Data* pd1 = new Data(2000,11,1);
	Data* pd2 = new Data(2000,8,1);
	cout << compare(pd1,pd2) << endl;
}

在这里插入图片描述
但是比较的结果并不对,这是因为这里比较的只是指针变量,比较的是地址标号,所以应该处理一下,那么有以下两种方法
方法一:重载一版指定出参数的类型(参数的匹配)


bool compare(const Data*& x, const Data*& y)
{
	return x->_year < y->_year;
}

同时这里体现了模板的匹配原则:

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板;
    也就是有现成的用现成的,没现成的用最匹配的

方法二:模板特化

template<class T>

bool Compare(const T x, const T y)
{
	return x < y;
}
template< >
bool Compare<Data*>(Data* x, Data* y)
{
	return x->_year < y->_year;
}

在这里插入图片描述
函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
    通过调用截图可以看出,特化也叫专用版本,有特化使用特化,没特化使用现成的函数,现成的函数没有就用普通函数模板

类模板特化

类模板特化步骤与函数模板特化基本类似
普通类模板

template<class T,class H>
class Data
{
	Data(const T& x, const H& y)
		:_x(x)
		,_y(y)
	{}
private:
	T _x;
	H _y;	
};

全特化

template<>
class Data<char ,int>
{
	Data(const char& x, const int& y)
		:_x(x)
		,_y(y)
	{}
private:
	char _x;
	int _y;
};

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

偏特化

1.将模板参数类表中的一部分参数特化。

template<T>
class Data<T,int>
{
	Data(const T& x, const int& y)
		:_x(x)
		,_y(y)
	{}
private:
	T _x;
	int _y;
};

2.针对模板参数更进一步的条件限制。

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

模板的分离编译

分离编译就是把一个项目分为若干个源文件,再根据不同的功能放到不同的分为同一个功能的.cpp和.h文件等等。查看 .h 文件方便交接整体框架(放结构定义、函数声明等),.cpp文件可以了解具体功能(功能的实现和函数的定义等),所以分离编译的优势是为了方便维护

但若是声明在.h中,定义在.cpp中,在另一个.cpp中调用就会发链接错误

.h

template<class T>
T sub(T& x,T& y);
.cpp

#include "sub.h"
T sub(T& x, T& y)
{
	return x - y;
}

在使用模板时,大都包括头文件(.h),实现文件(.cpp),测试文件(test.cpp);在编译阶段,每一个cpp文件都是相对独立的,并不知道另一个编译文件的存在,外部调用,会在链接阶段进行重定位;而模板的转换发生在编译时,模板的实例化只能发生在本编译文件的调用。分离编译时出现非本编译文件的模板调用(test.cpp调用,但是没在test中定义),只能等待链接时重定位,但是模板并没有实例化,所以会出现链接出错
在这里插入图片描述
注意:声明和定义分离编译,定义一个模板类型的类指针是可以的,因为他是用这个名称,并不是调用所以有声明就可以

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值