关于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<>来管理。