1、函数模板
模板的定义:
常规定义一个函数用于交换两个数 ,两个数可能是 int 或者 double ,可以这么实现
void swap(int &x, int &y);
void swap(double &x, double &y);
但是如果种类比较多的话,就需要写很多个参数,而这几个函数的样式基本一样,从中总结出共性,如下结构
template <typename T>
void swap(T &x ,T &y){
T temp ;
temp =x;
x=y;
y=temp;
}
以上也就是函数模板的形式,要注意的是 模板的声明需要将模板的头一起带上,如下:
template <typename T>
void swap(T &x, T &y);
模板的使用:
定义了一个如上的模板,要使用它的时候 ,以下是main函数中
int a = 1;
int b = 2;
double c = 3.0;
double d = 4.0;
my_swap(a, b);
my_swap(c, d);
cout << " a = " << a << " b = " << b << endl;
cout << " c = " << c << " d = " << d << endl;
从结果看确实实现了功能,这可比使用函数重载好用多了
- 需要注意的是在std中也定义一个 swap ,直接使用的话会用冲突问题,所以这里我改了个名字
结合模板和重载 :
模板和重载各有特色,如果能结合两个一起使用,效果更好
- 观察结果可以发现,模板用于函数结构完全一样的地方,仅仅是类型上的不同,使用泛型可以根据使用条件,自动生成函数。这和同名函数重载查找匹配函数很像。但是如果参数数目不一样的话,就没有办法使用模板结构。
以下结合两种使用:
template <typename T>
void my_swap(T &x, T &y);
template <typename T>
void my_swap(T &x, T &y, int n);
- 模板中并非全部的类型都是泛型,也可以有普通的数据类型
模板的局限性
- 虽然模板中的是泛型,但是有些类型可能是不能使用的,比如使用泛型进行大小比较,但是具体调用的时候,泛型是用 结构体带入,可能就会出问题。这只是一个例子,所以使用结果体还是需要根据需要做好判断。
具体化
具体化 分成3重种 :隐式实例化、显示实例化、显示具体化
- 隐式实例化: 在上述使用的函数模板中,并没有给出实际的函数,只是给定生成一个函数的规则,编译器在编译期间根据实际情况,实例化生成一个函数调用,但是我们没看见,这是隐式实例化 。
- 显示实例化:在使用模板中,我们希望先按照规则生成一个模板来使用。
tempalte void my_swap<int>(int , int );
不过由于是模板的实例化,在这之前,必须先有函数模板的存在,否则会出现如下错误
- 显示具体化:
template <> void my_swap<job>(job &x, job &y);
显示具体化和显示实例化看起来非常像,但是存在的理由是什么呢。目前实际中我还没有看到,只能猜测,如果一个用结构实现的my_swap(job &, job &);结构和普通的模板一样,显然不能直接使用模板实例化使用,显示具体化的优先级更高,定义了显示具体化后可以在模板实例化之前调用正确的显示具体化函数。
同一个函数名,可以给同时存在,这几种形式,普通非模板函数、模板函数、显示具体化模板函数 、其他重载函数。
- 调用优先级: 普通函数 >显示具体化模板 >普通模板函数