在类外定义成员函数。在类外定义成员函数时仍然需要带上模板头。
类模板在实例化时必须显式地指明数据类型,编译器不能根据给定的数据推演出数据类型。
而对于函数模板,调用函数时可以不显式地指明实参(也就是具体的类型)
这种通过函数实参来确定模板实参(也就是类型参数的具体类型)的过程称为模板实参推断。
对于函数模板,类型转换则受到了更多的限制,仅能进行「const 转换」和「数组或函数指针转换」,其他的都不能应用于函数模板。
当函数形参是引用类型时,数组不会转换为指针
模板所支持的类型是宽泛的,没有限制的,我们可以使用任意类型来替换,这种编程方式称为泛型编程(Generic Programming)。
交换两个数组唯一的办法就是逐个交换所有的数组元素
C++ 允许对函数模板进行重载,程序员可以像重载常规函数那样重载模板定义。
显式地指明实参时可以应用正常的类型转换
函数模板显示具体化
//函数模板
template<class T> const T& Max(const T& a, const T& b);
//函数模板的显示具体化(针对STU类型的显示具体化)
template<> const STU& Max<STU>(const STU& a, const STU& b);
显示具体化优先于常规模板,而非模板函数优先于显示具体化和常规模板。
类具体化
当在类的外部定义成员函数时,普通类模板的成员函数前面要带上模板头,而具体化的类模板的成员函数前面不能带模板头。
部分显示具体化(只能用于类),和具体显示化一样,没有被具体化的类型参数需要
emplate<typename T2> //这里需要带上模板头
void Point<char*, T2>::display() const{
cout<<"x="<<m_x<<" | y="<<m_y<<endl;
}
在函数模板中使用非类型参数
交换数组内容
template<typename T, unsigned N> void Swap(T (&a)[N], T (&b)[N]){
T temp;
for(int i=0; i<N; i++){
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
T (&a)[N]表明 a 是一个引用,它引用的数据的类型是T [N],也即一个数组
非类型参数的限制
当非类型参数是一个整数时,传递给它的实参,或者由编译器推导出的实参必须是一个常量表达式
非类型参数是一个指针(引用)时,绑定到该指针的实参必须具有静态的生存期;换句话说,实参必须存储在虚拟地址空间中的静态数据区。局部变量位于栈区,动态创建的对象位于堆区,它们都不能用作实参。
通过类模板创建对象时并不会实例化所有的成员函数,只有等到真正调用它们时才会被实例化;如果一个成员函数永远不会被调用,那它就永远不会被实例化。