关于c++中的类模板,常见的形式为:
template<typename T> class className{ //... };
比如笔者在这里举一个例子:
#include <iostream> #include<vector> #include <stdexcept> template<typename T> class stack{ private: std::vector<T> elems; public: void push(T const&); void pop(); T top() const; bool empty() const{ return elems.empty(); } }; template<typename T> void stack<T>::push(T const& a){ elems.push_back(a); } template<typename T> void stack<T>::pop(){ if(elems.empty()){ throw std::out_of_range("stack<T>::pop empty"); } return elems.pop_back(); } template<typename T> T stack<T>::top()const{ if(elems.empty()){ throw std::out_of_range("stack<T>::top empty"); } return elems.back(); } int main(){ return 0; }
从上面的代码可以看出,为了定义成员函数,我们使用了下面的形式,比如:
template<typename T> void stack<T>::push(T const& a){ elems.push_back(a); }
本来在此处有一个问题的,也就是关于类成员函数的实现位置的问题,这个问题我们稍后会提到。
现在我们为了测试上面的例子可以使用下面的函数代码:
int main(){ stack<int> intStack; intStack.push(1); intStack.push(2); std::cout<<intStack.top()<<std::endl; stack<std::string> strStack; strStack.push("hello"); std::cout<<strStack.top()<<std::endl; return 0; }
我们有些时候会遇到这样的代码,比如:
stack<stack<int> > intStack;
在这种情况下,我们要注意就是两个“>”中间的那个空格,那个是必须的,否则的话,编译器会认为是“>>”。这一点大家写代码的时候要注意。
下面我们来看看类模板的特化:
当我们想特化一个类模板的时候,我们就需要用template<>开头,后面更上我们希望特化的代码。比如:
template<> class myStack<std::string>{ //... };
对于特化类模板而言,就如同编写普通的类成员函数一样,比如:
void myStack<int>::pop(int const& a){ //... }
下面我们呢来看看类模板的局部特化。
例如对于下面的代码:
template<typename T1,typename T2> class myClass{ /... };
而言,以下几种像是的局部特化都是正确 的:
//局部特化 两个参数一致 template<typename T> class myClass<T,T>{ //... }; //局部特化 第二个为int template<typename T> class myClass<T,int>{ //... }; //两个参数都为指针类型 template<typename T1,typename T2> class myClass<T1*,T2*>{ //... };
在下面的例子中:
myClass<int float>mif; //将使用<T1,T2> myClass<float,float>mif; //将使用<T> myClass<float,int>mif; //将使用<T,int> myClass<int*,double*>mp; //将使用<T1*,T2*>
大家要注意下面的错误代码,
myClass<int,int>m; myClass<int*,int*>m;
z这两个代码在上面的这个例子中都是错误的。前者因为会和myClass<T,T>产生二义性。后者会和myClass<T,T>产生二义性、使得编译器不知道应该匹配哪一个。
如果想解决上面的第二个二义性的话,我们可以专门特化下面的代码:
template<typename T> class myClass<T*,T*>{ //... };
接下来我们来看看预设模板参数:
我们先来看看代码再说,大家注意和上面的例子进行比较:
#include <iostream> #include<vector> #include <deque> #include <cstdlib> #include <stdexcept> template<typename T,typename CONT=std::vector<T> > class stack{ private: CONT elems; public: void push(T const&); void pop(); T top() const; bool empty() const{ return elems.empty(); } }; template<typename T,typename CONT> void stack<T,CONT>::push(T const& a){ elems.push_back(a); } template<typename T,typename CONT> void stack<T,CONT>::pop(){ if(elems.empty()){ throw std::out_of_range("stack<T>::pop empty"); } return elems.pop_back(); } template<typename T,typename CONT> T stack<T,CONT>::top()const{ if(elems.empty()){ throw std::out_of_range("stack<T>::top empty"); } return elems.back(); } int main(){ try{ stack<int> intStack; stack<double,std::vector<double> > douStack; //注意,这里千万不能写下面的这一行代码: // stack<double,std::vector<int> > douStack; intStack.push(1); std::cout<<intStack.top()<<std::endl; douStack.push(1.1); std::cout<<douStack.top()<<std::endl; }catch(std::exception const& ex){ std::cerr<<ex.what()<<std::endl; return EXIT_FAILURE; } return 0; }
我们使用
stack<double,std::vector<double> > douStack;
来声明了一个double stack,他的内部使用的是std:eque<>来管理。