与函数相似,类也可以被一种或者多种类型参数化。
现在就让让我们看个简单的列子
#include<iostream>
#include<vector>
template<typename T>
class Stack {
public:
void push(const T& elem);
void pop();
private:
std::vector<T>vec;
std::size_t elemnumber;
};
template<typename T>
void Stack<T>::push(const T& elem) {
if (elemnumber == vec.size())
throw std::out_of_range("Stack is fill");
vec.push_back(elem);
}
template<typename T>
void Stack<T>::pop() {
if (elemnumber == 0)
throw std::out_of_range("Stack is empty");
vec.pop_back(); //只删除栈顶元素
}
int main(void)
{
Stack<int>stack;
stack.pop();
}
从上文代码可以看出,定义类模板的成员函数,必须指定该成员函数是一个函数模板,而且还需要使用类模板的限定符。
template<typename T>
void Stack<T>::push(const T& elem) {
if (elemnumber == vec.size())
throw std::out_of_range("Stack is fill");
vec.push_back(elem);
}
template<typename T>
void Stack<T>::pop() {
if (elemnumber == 0)
throw std::out_of_range("Stack is empty");
vec.pop_back(); //只删除栈顶元素
}
那么我们怎么使用使用这个类模板,怎么使用成员函数呢?
int main(void)
{
Stack<int>stack; //模板的实例化,用模板实参int去替换模板参数T
stack.pop(); //成员函数的调用
}
记住:实例化模板只实例化了成员函数的声明,具体调用哪个成员函数,才去实例化该成员函数的代码,这样可以节省时间与空间。
模板的实例化也可以理解为是模板的特化,通过模板的特化,可以优化基于某特定类型的实现,或者可以克服某种特定类型在实例化时所表现的不足(该类型没用提供某种操作,而模板特化有时候会隐时的支持)。模板的特化有两个重要的特化,首先让我来看看模板的全特化
template<> //注意尖括号里面没用模板参数
class Stack<int> { //非类型的模板
public:
void push(int elem);
void pop();
private:
std::vector<int>vec;
std::size_t elemnumber;
};
void Stack<int>::push(int elem) {
if (elemnumber == vec.size())
throw std::out_of_range("Stack is fill");
vec.push_back(elem);
}
void Stack<int>::pop() {
if (elemnumber == 0)
throw std::out_of_range("Stack is empty");
vec.pop_back();
}
int main(void)
{
Stack<int>stack;
stack.pop();
}
- 全特化也叫显示特化,它为模板提供了一种使模板参数可以全局替换的实现,而没用剩下模板参数。
- 全特化的实现其实并不需要与主模板泛型实现有任何关联,它只和主模板的名称相关。言外之意就是说,全特化的模板里面的成员函数可以和主模板的成员函数不一样。
- 指定的模板实参列表必须和相应的模板参数列表对应。
- 可以用全局模板的特化来代替泛型模板实例化的某个实体。
全局特化的实质:全局特化的作用对象不是模板 ,它就类似与重新声明类或者函数。
全局特化通常是很有用的,但是有时我们更希望把类模板特化成一个“针对模板实参”的类家族,而不是针对“一个具体实参列表”的全局特化,因此我们引入了偏特化(局部特化)
template<typename T>
class Stack<T, std::size_t> {
public:
void push(const T& elem);
void pop();
private:
std::vector<T>vec;
std::size_t elemnumber;
};
template<typename T>
void Stack<T, std::size_t>::push(const T& elem) {
if (elemnumber == vec.size())
throw std::out_of_range("Stack is fill");
vec.push_back(elem);
}
template<typename T>
void Stack<T, std::size_t>::pop() {
if (elemnumber == 0)
throw std::out_of_range("Stack is empty");
vec.pop_back();
}
int main(void)
{
Stack<int,std::size_t>stack;
stack.pop();
}
其实我们还可以这样:
template<typename T>
class Stack<T, T> {
};
template<typename T1>
class Stack<T1, int> {
};
template<typename T1,typename T2>
class Stack<T1*, T2*> {
};
template<typename T1,typename T2> //error
class Stack<T1, int> { //必须出现 T2 ,不然无法推断出T2的类型
};
局部特化声明的实参必须和基本模板的相应参数在种类上是匹配的。
-
局部特化的实参必须和基本模板的形参类型相匹配。
-
template<typename T,int N=5> class A; template<typename T> class A<int, T>; //error
-
局部特化的参数列表不能有缺省实参,但局部特化仍然可以使用基本类模板的缺省实参。
template<typename T,int N=5>
class A;
template<typename T=int> //error
class A<T, 5>;
局部特化的非类型实参只能是非类型值,或者是普通的非类型模板参数;而不能是更复杂的依赖型表达式。
template<typename T,int N=5>
class A;
template<int I>
class A<int,I*2> //error
局部特化的模板参数列表不能和基本模板的参数列表完全相同。
template<typename T1,typename T2>
class A;
template<typename T,typename U>
class A;
那么问题来了,在实际中我既实例化的主模板,有实例化了全特化的模板的,在调用成员函数时到底该调用哪一个呢?