模板:
模板分为函数模板和类模板,c++模板可以中的模板可以简单理解成给程序建立一个模型(模板),当我们需要调用某种函数时,编译器会自动使用和模型一样的函数进行执行。
在了解模板函数之前我们先了解以下这个实例:
比如我们要实现一个可以任意类型的加法运算,如下列代码:
void Add(int& left, int& right)
{
return = left + right;
}
void Add(double& left, double& right)
{
return = left + right
}
void Add(char& left, char& right)
{
return = left + right
}
…
这是使用函数重载实现的一个任意类型的加法运算,但是这个函数有几个不好的地方:
1、重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时,就需要增加对应的函数,比较麻烦。
2、 代码的可维护性比较低,一个出错可能所有的重载均出错。
因此我们可以使用函数模板来实现这个函数:
函数模板代表了一类函数,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
函数模板的基本格式:
template<typename T1,typename T2…,typename T3>
返回值类型 函数名(参数列表){}
这里面的typename的功能与class基本相似都可以使用
template<class T1,class T2…,class T3>
下来我们试着将上面的加法函数使用函数模板形式写出来:
template
T Add(T left, T right) {
return left + right;
}
int main()
{
Add(1, 2);
Add(1.2, 2.3);
Add(1, 1.2);
return 0;
}
通过编译我尝试使用main函数实现了
Add(1, 2);以及Add(1.2, 2.3);
但是当调用Add(1, 1.2);时,编译器报错
因此我们得出,当函数调用不同的类型时,如果我们的类型(T)只给出一种,那么编译器会报错。
因此这个时候就需要给出两种类型,如下
template <class T, class T1>
T Add( T left, T1 right) {
return left + right;
}
int main()
{
Add(1, 2);
Add(1, 2.0);
return 0;
}
这样就可以实现任意类型的加法运算了。
在函数模板中使用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
隐式实例化:让编译器根据实参推演模板参数的实际类型
对于函数模板,编译器在检测到对函数模板进行实例化,编译器会通过对参数类型进行推演来生成代码如果编译器发现参数类型不匹配时,不会进行隐式类型转化,直接报错。
显示实例化:在函数名后的<>中指定模板参数的实际类型
明确指出模板参数列表中T的实际类型,发现类型不匹配后,会尝试类型转化
如:
模板参数的匹配原则:
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
例如下面函数:
int Add(int left, int right)
{
return left + right;
}
template
T Add(T left, T right)
{
return left + right;
}
2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数,那么将选择模板函数。
3.模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
就比如:
int Add1(int a, int b) {
return a + b;
}
int main() {
Add1(1, 2.3);//对于正常的函数调用,如果编译器检测到参数不匹配 //时,会尝试进行隐式类型转化,但是模板函数直接报错
}
下面我们来看看类模板:
类模板的定义格式:
template <class T1, class T2, …, class Tn>
class 类模板名
{
// 类内成员定义
};
类模板的实例化:
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。