目录
一、函数模板
1、为什么要使用函数模板?
2、函数模板的定义及其使用
3、函数模板的实现原理
4、特例:同名非模板函数能和同名模板函数 同时存在
二、类模板
1、类模板格式
2、使用类模板创建对象
3、类外定义成员函数
三、模板特化
1、为什么会有模板特化?
2、模板全特化的实现
3、模板偏特化的实现
我们想实现两个数的交换,但是有的时候,数据类型是int,有的时候是float,实现的方法是一样的,只是数据类型的不同
每增加一种类型就要增加一个函数,未免过于麻烦
所以我们引入了函数模板
函数模板根据给定的实参类型产生 函数的指定类型版本
格式如下
template // template
返回值类型 函数名(形参列表)
{
//函数体
}
/*
假设要定义一个交换两个数的函数模板
*/
template //声明使用函数模板,下面紧跟的函数就可以使用数据类型T了
void Swap(T& x, T& y) //T 代表某种未知的数据类型,根据输入的实参决定
{
T tmp = x;
x = y;
y = tmp;
}
使用的方式有两种
第一种,隐式实例化—— 让编译器自己去推导实参类型。
第二种,显式实例化—— 自己显式指定传入的类型。如果传入的类型和指定类型不一致,会被强制转化为指定的类型,比如指定的是int类型,但传入的是double类型,那么double就会被强制转化成int
函数模板类似于一套模具,使用指定的模具能够创造出指定类型的饼干
在编译阶段,如果未指定类型,那么编译器就会根据传入的实参类型,产生一份对应的类型的函数
我们再通过反汇编来能够发现,每次实例化生成的函数都是不一样的!!
而且编译器会自动加上推导结果
下面这种情况是允许的
传入的参数是int类型时候,优先调用非模板函数
因为模板函数需要先实例化再调用,但是非模板函数能够直接调用,手边既然有工具为什么还要去商店里买呢??
int i1 = 10, i2 = 20;
Swap(i1, i2);
但是如果传入的参数是double类型,由于没有合适的非模板函数,那么就只能调用模板实例化的函数
double d1 = 10.1, d2 = 10.2;
Swap(d1, d2);
类模板和函数模板类似,不同之处在于 类模板不会自动推导参数类型,只能显式指定
template
class Person
{
public:
void SetWeight(const T& w);
private:
T* weight;
};
类模板没有指定类型的话,就会报错
类内定义成员函数没有什么特别要注意的地方,但是类外定义就需要注意了
以两数之和相加为例,如果是两个int类型或者char类型的数相加,完全没有问题,因为C语言库已经重载了 运算符“+”的int版本和char版本,那如果是两个对象相加呢?这个时候就会出现问题,自定义类型相加需要自己重新实现运算符“+”的重载。
对于这种特殊情况,我们需要另外拎出来单独处理,这样的我们称之为“模板特化”(可以理解为特殊化处理)
全特化:所有的虚拟类型都需要指定类型
关于模板特化,我们只需要将原版复制一份,拿来修改即可,模板特化以后的函数 和 原本的函数模板如下:
原本的函数模板:
特化以后的函数模板:
偏特化:只有部分虚拟类型需要指定类型
函数原版如下
模板偏特化如下
提醒:模板特化不仅仅可以用于函数模板,类模板(结构体模板)一样适用
// 模板
template
struct Hash
{}
// 模板特化
template<>
struct Hash
{}