函数模板
函数模板的概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
函数模板的格式
template <typename T1, typename T2, ....... typename Tn>
返回值类型 函数名(参数列表){} typename 也可以用class代替,两者是一样的。
template <class T>
void Add(T& lest, T& right)
{
return left + right;
}
函数模板的原理
一句话,就是帮助我们解决重复性工作的模具。模板是一个蓝图,它本身不是函数,是编辑一起用使用方式产生特定具体类型函数的模具,所以模板就是讲本来应该我们做的重复的事情交给了编译器。
int Add(int left, int right)
{
return left + right;
}
double Add(double left, double right)
{
return left + right;
}
char Add(char left, char right)
{
return left + right;
}
int main()
{
Add(1, 2);
Add(1.2, 2.3);
Add('1', '2');
return 0;
}
加法函数,不同的数据类型,就需要调用不同的方法,来完成同一个功能,函数的重复率太高,而且维护起来比较麻烦,这个时候我们就可以用模板来实现这个加法函数。
template <class T>
T Add(T lest, T right)
{
return left + right;
}
int main()
{
Add(1, 2); // int
Add(1.2, 2.3); // double
Add('1', '2'); // char
return 0;
}
函数模板的实例化
函数模板的实例化,也就是将函数模板参数具体化。
隐式实例化
template <class T>
T Add(T lest, T right)
{
return left + right;
}
int main()
{
// 对函数模板进行实例化
Add(1, 2); // int
Add(1.2, 2.3); // double
Add('1', '2'); // char
return 0;
}
编译器在编译阶段,用户对哈数模板实例化之后,编译器会对实参类型进行推演,根据推演的结果来确定模板参数列表中T的实际类型,根据确定的T具体类型来生成处理具体类型的函数。类型转换只是在编译器编译时进行,编译器在生成代码时不会进行隐式类型转化。
Add(1, 2.3);
在隐式实例化中,该语句不能编译通过,在编译期间,编译器对模板类型进行推演室,无法确定T的具体类型,只能报错。在模板中,编译器一般不会进行类型转换。
解决上面的方法有两种:
- 用户自己强转
Add(1, (int)2.3);
- 采用显式实例化
显式实例化
在函数名后面的<>中指定函数模板的实际类型
Add<int>(1,2.3); T--->int
Add<double>(1,2.3); T--->double
编译器在编译代码期间,看到对函数模板进行显式实例化之后,直接根据显式实例化的T的具体类型直接生成代码。如果不匹配,编译器还可能进行隐式实例化,否则就会报错。
函数模板参数的匹配原则
- 一个非模板函数可以和一个同名的函数模板同时存在,该函数模板还可以被实例化为这个非模板函数
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非模板函数匹配,编译器不需要特化
Add<int>(1, 2); // 调用编译器特化的Add版本
}
如果有对应的非模板函数,编译器会优先调用,不需要再将函数模板进行实例化后调用。模板函数不允许自动转换类型,但普通函数可以自动转换类型。
- 函数模板可以被实例化成不同类型的参数
template<class T1, class T2>
T1 Add(const T1& left, const T2& right)
{
return left + right;
}
int main()
{
Add(1, 2); T1---->int
Add(1, 2.0); T1---->int, T2---->double
return 0;
}
类模板
类模板的定义格式
template<class T1, class T2, ....., class Tn>
class 类模板名
{
// 类内成员定义
};
类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后加<>,然后将实例化的类型放在<>之中,类模板名字不是真正的类,而实例化的结果才是真正的类。
Vector<int> s1;//Vector 类模板名(不是真正的),Vector<int >才是类型
Vector<double> s2;