本文介绍函数模板的概念、用途以及如何创建函数模板和函数模板的使用方法......
函数模板定义的一般形式如下:
template<类型形式参数表>
返回类型 函数名(形式参数表)
{
... //函数体
}
之前我们知道的交换两个变量的方法有宏定义、函数,这两种方式都能实现两个变量的交换,但是各有各的优缺点
宏定义:
-优点:
代码复用,适合所有的类型
- 缺点:
缺少类型检查,宏在预处理阶段就被替换掉,编译器并不知道宏的存在
函数:
- 优点:
真正的函数调用,编译器对类型进行检查
- 缺点:
类型不同需要重复定义函数,代码无法复用
上边两种方式都各有利弊,但是在C++中,存在泛型编程的概念:即不考虑具体数据类型的编程方式(如下)
对于函数体相同且函数的参数个数相同而参数类型不同的一系列函数而言,都是可以用函数模板来代替的,只需要定义一次函数模板即可。
函数模板通过template与typename(class代替 效果一致)两个关键字来定义,如下
上边就定义了一个变量交换的函数模板,在使用函数模板时有两种方式
- 自动类型推到调用 Swap(a, b)
- 具体类型显示调用 Swap<int>(a, b)
template<typename t>
t max(t a, t b)
{
return a > b ? a : b;
}
int main()
{
int a = 0;
//模板参数列表中只有一种数据类型,传入的实参也只有⼀种数据类型
//可以直接由函数模板生成模板函数
//隐式实例化
a = max(1, 2);
//模板参数列表中只有⼀种数据类型,但是此时传入的实参有两种数据类型
//在调用时需要强制的类型转换,保证实参的类型个数与模板参数列表的类型个数⼀致
//由函数模板生成模板函数时编译器不能做到自动的隐式转换,需要显示的强制转换
a = max(1, (int)2.0);
a = max((double)1, 2.0);
//显式转换也可以以尖括号形式的语法进行转换
//显式实例化
a = max<int>(1, 2.0);
a = max<double>(1, 2.0);
return 0;
}
需要注意的是:函数模板不支持隐式类型转换
还有一招:显式具体化
函数模板的显式具体化是对函数模板的重新定义,具体格式如下所示:
template< > 函数返回值类型 函数名<实例化类型>(参数列表)
{
//函数体重新定义
}
显式实例化只需要显式声明模板参数的类型而不需要重新定义函数模板的实现,而显式具体化需要重新定义函数模板的实现。
//定义交换两个数据的函数模板,示例如下
template<typename T>
void swap(T& t1, T& t2)
{
T temp = t1;
t1 = t2;
t2 = temp;
}
//但现在有如下结构体定义
struct Student
{
int id;
char name[20];
float score;
};
//现在我们只想交换两个学生的id,那么我们就可以用显式具体化解决这个问题
template<> void swap<Student>(Student& s1, Student& s2)
{
int temp = s1.id;
s1.id = s2.id;
s2.id = temp;
}
如果函数有多个原型,则编译器在选择函数调用时,非模板函数优先于模板函数,显式具体化模板优先于函数模板,例如下面三种定义:
void swap(int&, int&); //直接定义
template<typename T>void swap(T& t1, T& t2); //模板定义
template<> void swap<int>(int&, int&); //显式具体化
如何理解呢?
int a, int b存在swap(a, b)调用,会优先调用直接定义的函数;如果没有,则调用显式具体化 ;两者都不存在,这时候才去调用模板函数。
函数模板也是可以重载的,和函数重载差不多,这里就不细说了。
使用函数模板时需要注意的问题:
1) < > 中的每一个类型参数在函数模板参数列表中必须至少使用一次。
例如,下面的函数模板声明是不正确的。
template<typename T1, typename T2>
void func(T1 t)
{
}
函数模板声明了两个参数 T1 与 T2,但在使用时只使用了 T1,没有使用 T2。
2) 全局作用域中声明的与模板参数同名的对象、函数或类型,在函数模板中将被隐藏。
例如:
int num;
template<typename T>
void func(T t)
{
T num;
cout << num << endl; //输出的是局部变量num,全局int类型的num被屏蔽
}
在函数体内访问的 num 是 T 类型的变量 num,而不是全局 int 类型的变量 num。(局部优先)
3) 函数模板中声明的对象或类型不能与模板参数同名。
例如:
template<typename T>
void func(T t)
{
typedef float T; //错误,定义的类型与模板参数名相同
}
4) 模板参数名在同一模板参数列表中只能使用一次,但可在多个函数模板声明或定义之间重复使用。
例如:
template<typename T, typename T> //错误,在同一个模板中重复定义模板参数
void func1(T t1, T t2)
{}
template<typename T>
void func2(T t1)
{}
template<typename T> //在不同函数模板中可重复使用相同的模板参数名
void func3(T t1)
{}
5) 模板的定义和多处声明所使用的模板参数名不是必须相同。
例如:
//模板的前向声明
template<typename T>
void func1(T t1, T t2);
//模板的定义
template<typename U>
void func1(U t1, U t2)
{
}
6) 如果函数模板有多个模板参数,则每个模板参数前都必须使用关键字 class 或 typename 修饰。
例如:
template<typename T, typename U> //两个关键字可以混用
void func(T t, U u)
{}
template<typename T, U> //错误,每一个模板参数前都必须有关键字修饰
void func(T t, U u)
{}
这次的文章也是到这里就结束了。
如果觉得有用的话,点个赞支持一下吧!
谢谢各位大佬