函数模板是通用的函数描述,使用任意类型(泛型)来描述函数。
编译的时候,编译器推导实参的数据类型,根据实参的数据类型和函数模板,生成该类型的函数定义。
生成函数定义的过程被称为实例化。
下面是一个简单的例子
void Swap(T &a,T &b)
{
T tmp=a;
a=b;
b=tmp;
}
void test()
{
int a=1;
int b=2;
swap(a,b);
swap<int>(a,b);
}
注意
1)可以为类的成员函数创建模板,但不能是虚函数和析构函数。
2)使用函数模板时,必须明确数据类型,确保实参与函数模板能匹配上。
3)使用函数模板时,推导的数据类型必须适应函数模板中的代码。
4)使用函数模板时,如果是自动类型推导,不会发生隐式类型转换,如果显式指定了函数模板的数据类型,可以发生隐式类型转换。
5)函数模板支持多个通用数据类型的参数。
6) 函数模板支持重载,可以有非通用数据类型的参数。
针对第四点,我们看如下代码
template<typename T>
T ADD(T a,T b)
{
return a+b;
}
void test()
{
int a=1,b=2;
char c=20;
int sum1=ADD(a,b); //隐式显示都可以
int sum2=ADD<int>(a,c);//必须显式调用,char可以隐式转换成int
}
函数模板的具体化
具体化(特例化、特化)的语法:
template<> void 函数模板名<数据类型>(参数列表)
template<> void 函数模板名 (参数列表)
{
// 函数体。
}
编译器使用各种函数的规则:
1) 具体化优先于常规模板,普通函数优先于具体化和常规模板。
2)如果希望使用函数模板,可以用空模板参数强制使用函数模板。
3)如果函数模板能产生更好的匹配,将优先于普通函数。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class Object
{
public:
string m_name;
Object(string name) :m_name(name) {}
};
void Swap(int a, int b) // 普通函数。
{
cout << "使用了普通函数。\n";
}
template <typename T>
void Swap(T a, T b) // 函数模板。
{
cout << "使用了函数模板。\n";
}
template <>
void Swap(int a, int b) // 函数模板的具体化版本。
{
cout << "使用了具体化的函数模板。\n";
}
int main()
{
Swap(1, 2); //普通函数优先于具体化和常规模板
Swap<>(1, 2); //使用一对空尖括号代表强制使用具体化函数模板
Swap('c', 'd');
Object obj1("张三"), obj2("李四");
Swap(obj1, obj2); //函数模板
return 0;
}