区别
特性 | 类模板 | 函数模板 |
---|---|---|
定义 | 定义一组类,其中某些成员或者继承关系可以是通用类型或值。 | 定义一个函数,其类型参数可以被通用类型或值所代替。 |
语法 | template class ClassName { /* ... */ }; | template T functionName(T arg) { /* ... */ } |
实例化 | 对象创建时需要显式或隐式指定类型参数,如 ClassName<int> myObject; 。 | 调用时可以显式或隐式推导类型,如 functionName<int>(arg); 或 functionName(arg); |
特化 | 可以部分或完全特化模板来处理特定类型的特殊行为。 | 可以特化模板函数以适应特定类型。 |
默认模板参数 | 支持为模板参数设置默认值。 | 支持为模板参数设置默认值。 |
成员函数 | 可以包含模板和非模板成员函数。 | 不适用,因为它本身是函数。 |
隐式推导 | 类模板参数不能隐式推导(直到C++17引入类模板参数推导)。 | 函数模板参数可以通过函数参数隐式推导。 |
范围 | 定义类的整体结构,包括数据成员和成员函数。 | 仅定义函数的操作。 |
编译生成 | 构建类模板时,只有在实例化的时候才会生成代码。 | 函数模板在每次调用时,如果有新的类型,都会生成新代码。 |
使用场景 | 用于创建可以操作不同类型数据的类,如容器类 vector、list等。 | 用于创建可以用于不同类型的操作的函数,如sort、max等。 |
动态多态支持 | 类模板不支持动态多态,因为模板的所有决议都是在编译时完成的。 | 不适用,因为模板函数没有动态多态的概念。 |
模板参数类型 | 类型名称(Typename)或非类型参数(整型常数,枚举,指针等)。 | 同类模板。 |
模板代码重用 | 类模板支持通过继承、组合等方式代码重用。 | 函数模板通过过载和特化重用代码。 |
编译错误提示 | 错误可能比较复杂,因为涉及到类的成员的多个实例化。 | 错误通常更直接清楚,涉及特定的函数调用。 |
函数模版
概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时由编译器根据实参类型产生函数的特定类型版本。在编译器编译阶段,对于函数模板的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如,当用int类型使用函数模板时,编译器通过对实参类型的推演,将T确定为int类型,然后产生一份专门处理int类型的代码,对于double类型也是如此。
函数模板的格式
typename是用来定义模板参数的关键字,也可以用class代替,但是不能用struct代替。
template<typename T1,typename T2,…,typename Tn>
返回类型 函数名(参数列表)
{
//函数体
}
函数模板的实例化
隐式实例化:让编译器根据实参推演模板参数的实际类型
#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{
return x + y;
}
int main()
{
int a = 10, b = 20;
int c = Add(a, b); //编译器根据实参a和b推演出模板参数为int类型
int d = 9;
double e = 10.0;
int f = Add(c, d); //编译失败,由显示解决
return 0;
}
显示实例化:在函数名后的<>中指定模板参数的实际类型
int main()
{
int a = 10;
double b = 1.1;
int c = Add<int>(a, b); //指定模板参数的实际类型为int
return 0;
}
类模板
定义格式
template<class T1,class T2,…,class Tn>
class 类模板名
{
//类内成员声明
};
template<class T>
class Score
{
public:
void Print()
{
cout << "数学:" << _Math << endl;
cout << "语文:" << _Chinese << endl;
cout << "英语:" << _English << endl;
}
private:
T _Math;
T _Chinese;
T _English;
};
// 类模板中的成员函数若是放在类外定义时,需要加模板参数列表
template<class T>
class Score
{
public:
void Print();
private:
T _Math;
T _Chinese;
T _English;
};
// 类模板中的成员函数在类外定义,需要加模板参数列表
// 类模板不支持分离编译,即声明在xxx.h文件中,而定义却在xxx.cpp文件中
template<class T>
void Score<T>::Print()
{
cout << "数学:" << _Math << endl;
cout << "语文:" << _Chinese << endl;
cout << "英语:" << _English << endl;
}
类模板实例化
类模板实例化需要在类模板名字后面根<>,然后将实例化的类型放在<>中即可。
//Score不是真正的类,Score<int>和Score<double>才是真正的类
Score<int> s1;
Score<double> s2;
非类型模版参数
模板参数可分为类型形参和非类型形参。
类型形参: 出现在模板参数列表中,跟在class或typename关键字之后的参数类型名称。
非类型形参: 用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。非类型模板参数只允许使用整型家族。
template<class T, size_t N> //N:非类型模板参数
class StaticArray
{
public:
size_t arraysize()
{
return N;
}
private:
T _array[N]; //利用非类型模板参数指定静态数组的大小
};
int main()
{
StaticArray<int, 10> a1; //定义一个大小为10的静态数组
cout << a1.arraysize() << endl; //10
StaticArray<int, 100> a2; //定义一个大小为100的静态数组
cout << a2.arraysize() << endl; //100
return 0;
}