目录
1.直白理解函数模板:函数模板就是建立一个通用的函数,其参数类型和返回类型不具体指定,用一个虚拟的类型来代表。
3.注意:使用显示具体化之前,必须有一个与其对应的模板函数。
一、函数模板应用场景
假如需要编写一个程序交换两个数,但是在写程序过程中,可能需要定义多个函数,每个函数比较一种给定类型的值。
void Swap(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void Swap(double& a, double& b)
{
double temp;
temp = a;
a = b;
b = temp;
}
但是这两个函数的函数体一样,只是参数类型不一样,所有我们可以从这些函数中提炼出一个通用函数,而它又适用于多种不同类型的数据。此时就需要用到函数模板。
二、函数模板
1.直白理解函数模板:函数模板就是建立一个通用的函数,其参数类型和返回类型不具体指定,用一个虚拟的类型来代表。
2.函数模板的声明
第一种:template<typename 类型参数>
返回类型 函数名(模板形参表)
例如:
template <typename T>
void Swap(T& a, T& b);
第二种:template<class 类型参数>
返回类型 函数名(模板形参表)
例如:
template <class T>
void Swap(T& a, T& b);
template 是C++关键字,和函数一样,模板也有一系列参数。
类型参数一般用T这样的标识符来代表一个虚拟的类型,当使用函数模板时,会将类型参数具体化。
类型名T可以表示任意的int,double等类型
3.函数模板的代码
#include<iostream>
#include<stdio.h>
using namespace std;
template <typename T>
//类型名T可以表示任意的int,double等类型
void Swap(T & a, T & b)
{
T temp;
temp = a;
a = b;
b = temp;
}
int main()
{
int x = 2, y = 3;
Swap(x, y);
cout << x << " " << y << endl;//输出3 2
char x1 = 'a', y1 = 'b';
Swap(x1, y1);
cout << x1 << " " << y1 << endl;//分别输出b a
return 0;
}
int main()
{
int d1[] = { 1,2,3,4,5 };
int len = sizeof(d1)/sizeof(d1[0]);
cout << len;
return 0;
}
三、重载的模板
1.为什么要使用重载模板
需要多个对不同类型使用同一种算法的函数时,可使用模板。然而并非所有的类型都使用相同的算法。为满足这种需求,可以像重载常规函数定义那样重载模板定义。和常规重载一样,被重载的模板的函数特征标必须不同。
2.代码举例
在下面程序中定义了两个模板,原来模板的特征标为(T &,T &),新模板的特征标为(T *,T *,int)
或者(T [],T [],int),编译器见到第一个Swap()函数调用时,发现它有两个int参数,因此将它与原
来的模板匹配。但是第二次调用将两个int数组和一个int值作为参数,这与新模板匹配
#include<iostream>
template <typename T> //template <class T>与template <typename T> 一样
void Swap(T& a, T& b);//用于交换两个数
template <typename T>
void Swap(T* a, T* b, int n);//用于交换两个数组中的元素
using namespace std;
template <typename T>
void Swap(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template <typename T>
void Swap(T a[],T b[],int n)
//void Swap(T* a, T* b, int n)(与上面形式等价)
{
T temp;
for (int i = 0; i < n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
int main()
{
int x1=1, y1=2;
Swap(x1, y1);
cout << x1 << " " << y1 << endl;
int d1[] = { 1,2,3,4,5 };
int d2[] = { 6,7,8,9,10 };
int n = sizeof(d1)/sizeof(d1[0]);
Swap(d1, d2,n);
cout << d1[0] << " " << d1[1] << " " << d1[2] << " " << d1[3] << " " << d1[4] << endl;
cout << d2[0] << " " << d2[1] << " " << d2[2] << " " << d2[3] << " " << d2[4] << endl;
return 0;
}
四、显式具体化
1.为什么使用显式具体化
假设有如下模板函数:
template <typename T>
void f(T a,T b)
{...}
通常代码假定可执行哪些操作。例如,下面的代码假定定义了赋值,如果T为数组,这种假设不成了;
a = b;
同样,下面语句定义了<,但如果T为结构,该假设便不成立;
if (a > b)
另外,为数组名定义了运算符>,但由于数组名为地址,因此它比较的是数组的地址,而这可能不是我们
所希望的。
下面的语句假定为类型T定义了乘法运算符,但如果T为数组、指针或结构,这种假设便不成立。
T c = a * b;
所以为什么使用显式具体化:处理模板函数所不能处理的特殊情况。显式具体化显式具体化也是基于
函数模板的,只不过在函数模板的基础上,添加一个专门针对特定类型的、实现方式不同的具体化函数。
2.显示具体化的声明
template <>[函数返回类型][函数模板名]<实际类型列表>(函数参数列表)
template <> void Swap<job>(job& j1, job& j2);//第一种声明方式
template <> void Swap(job& j1, job& j2);//第二种声明方式,两种声明方式等价
其中void对应返回类型,Swap是函数模板名,<>里面的job是实际类型,()里面的job是参数类型
3.注意:使用显示具体化之前,必须有一个与其对应的模板函数。
比如:
template <typename T> //模板函数
void Swap(T & a, T & b);
template <> void Swap<job>(job& j1, job& j2);//显示具体化函数
4.常规模板,具体化模板,非模板函数的优先调用顺序
非模板函数(普通函数) > 具体化模板函数 > 常规模板
5.显示具体化的具体代码
#include<iostream>
using namespace std;
template <typename T>
void Swap(T& a, T& b);
struct job
{
char name[40];
double salary;
int floor;
};
template <> void Swap<job>(job& j1, job& j2);
template<typename T>
void Swap(T &a,T &b)//常规模板
{
T temp;
temp = a;
a = b;
b = temp;
}
template <> void Swap<job>(job& j1, job& j2)// 具体化模板(此处只选择将salary交换)
{
double temp_sal;
temp_sal = j1.salary;
j1.salary = j2.salary;
j2.salary = temp_sal;
}
int main()
{
/*int x1 = 1, y1 = 4;
Swap(x1, y1);
cout << x1 << " " << y1 << endl;*/
struct job x2 = { "ZhangSan",100.1,2 };
struct job y2 = { "LiSi",200.2,5 };
Swap(x2, y2);
cout << x2.name << " " << x2.salary << " " << x2.floor << endl;
cout << y2.name << " " << y2.salary << " " << y2.floor << endl;
return 0;
}
五、模板实例化(隐式实例和显式实例)
1. 什么是隐式实例化
定义好函数模板后,当程序需要该函数模板定义的具体类型的函数时,编译器会根据该函数模板生成具体类型的函数定义,这种实例化方式被称为隐式实例化。
例如:函数调用Swap(i, j)导致编译器生成Swap()的一个实例,该实例使用int类型。模板并非函数定义,但使用int的模板
实例是函数定义。同时编译器之所以知道需要进行定义,是由于程序调用Swap()函数时提供了int参数。
2.什么是显式实例化
直接命令编译器创建特定的实例,如Swap <int>()。其语法是,声明所需的种类——用<>符号指示类型,并且在声明前加上关键字template:
template[函数返回类型][函数模板名]<实际类型列表>(函数参数列表)
template void Swap <int>(int, int);
3.在程序中使用函数来创建显式实例化。
#include<iostream>
using namespace std;
template <class T> //模板的声明
T add(T a, T b);
template <class T>//模板的定义
T add(T a, T b)
{
return a + b;
}
int main()
{
double x = 6.7;
int y = 6;
cout << add<double>(x, y) << endl;//输出8.5
}
这里的模板和函数调用add(x,y)不匹配,因为该模板要求两个参数的类型相同都为T。但通过使用add<double>(x,y),可强制为double类型实例化,并将参数y强制转换为double类型,以便与函数add<double>(double ,double )的第二个参数匹配。
六、总结
模板函数
template <typename T> void Swap(T &,T &);
template <class T> void Swap(T &,T &);
显式具体化
template <> void Swap<int>(int& , int&);
template <> void Swap(int& , int&);
显式实例化
template void Swap<int>(int &,int &);