C++学习(6)——模板
系列文章目录
文章目录
- 系列文章目录
- 一、函数模板
- 1.函数模板的实例化
- (1)隐式实例化
- (2)显式实例化
- 2.模板函数的特性
- 二、类模板
一、函数模板
函数模板代表了一个函数系列,该函数模板与类型无关。在使用时被参数化,根据实参类型产生函数的特定类型版本。
使用格式如下,注意typename可以使用class替换!
template<typename T1, typename T2,…,typename Tn>
返回值类型 函数名(参数列表){}
template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
函数模板只是一个蓝图,它本身并不是函数,是编译器用来推演生成具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。
比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码。对于其他类型实参也是如此。
1.函数模板的实例化
函数模板实例化:编译器根据所传参数的类型推演模板类型并生成对应函数。
(1)隐式实例化
编译器通过参数类型推测模板参数的实际类型
template<class T>
T Add(T left, T right)
{
return left + right;
}
int main()
{
int a=0, b=1;
double c = 9.1, d = 5.2;
cout << Add(a, b) << endl;
cout << Add(c, d) << endl;
return 0;
}
注意:如果所传两个实参类型不同会产生错位,因为编译器无法推演出满版参数的类型到底是两种类型中的哪一个。
在隐式实例化中,编译器不会对参数类型不同的参数做隐式类型转换
template<class T>
T Add(T left, T right)
{
return left + right;
}
int main()
{
int a = 9;
double b = 2.3;
cout << Add(a, b) << endl;
return 0;
}
该情况有两种解决方法:1.使用显式实例化,直接指明模板参数的类型;2.对参数使用强制类型转换;
(2)显式实例化
在函数名后的<>中指定模板参数的实际类型,直接确定模板参数的类型,省去编译器推演过程。
int main(void)
{
int a = 10;
double b = 20.0;
// 显式实例化
Add<int>(a, b);
return 0;
}
当使用显式实例化时,编译器可以根据指定的参数类型对类型不同的参数自动进行隐式类型转换
所以上述参数类型不同引发的问题得以解决:
下述代码输出结果为11
template<class T>
T Add(T left, T right)
{
return left + right;
}
int main()
{
int a = 9;
double b = 2.3;
cout << Add<int>(a, b) << endl;
return 0;
}
2.模板函数的特性
(1) 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数;
// 专门处理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版本
(2).对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}
(3) 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
二、类模板
与函数模板格式相似,唯一不同是用来泛化类。
注意:T的作用域为整个类
格式如下:
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
使用类模板实现静态数组:
#define N 10
template<class T>
class test
{
public:
T& operator[](int idx)
{
if (idx < N)
{
return arr[idx];
}
}
private:
T arr[N];
};
int main()
{
test<int> t;
for (int i = 0; i < N; i++)
{
t[i] = i;
}
for (int i = 0; i < N; i++)
{
cout << t[i] << ' ';
}
cout << endl;
return 0;
}
类模板通常使用显式实例化,它没有机会使用隐式实例化,这要求我们在使用类实例化对象时要同时实例化模板类。