代码改变世界

C++ template 学习归纳2

2012-03-07 16:15  Rollen Holt  阅读(615)  评论(0编辑  收藏  举报

关于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:Open-mouthed smileeque<>来管理。

image