天天看点

《C++ Template. The Complete Guide》笔记之三 Class Templates

在粗粗地说了Function Templates之后,肯定要说说最主要的的Class Templates。因为是开始,所以肯定要说的明白点,清楚点:

#include <vector>

#include <stdexcept>

template <typename T>

class Stack {

private:

std::vector<T> elems; // elements

public:

void push(T const&); // push element

void pop(); // pop element

T top() const; // return top element

bool empty() const { // return whether the stack is empty

return elems.empty();

}

};

template <typename T>

void Stack<T>::push (T const& elem)

{

elems.push_back(elem); // append copy of passed elem

}

template<typename T>

void Stack<T>::pop ()

{

if (elems.empty()) {

throw std::out_of_range("Stack<>::pop(): empty stack");

}

elems.pop_back(); // remove last element

}

template <typename T>

T Stack<T>::top () const

{

if (elems.empty()) {

throw std::out_of_range("Stack<>::top(): empty stack");

}

return elems.back(); // return copy of last element

}

上面的这个呢就是作者给出的一个最基本的模板类的实现。和模板函数一样,可以看到熟悉的template关键字,<typename T>模板参数的类型标识符。这个实现的好处在于,不仅告诉了你如果申明一个模板类,同时还有如果实现这个模板类的接口。我们可以看到,除了这个Stack<T>::这个类型标识符在这里以外,他和一个模板函数是一样的。。通过这个例子可以发现,如果使用模板类也在其中了,std::vector<T> elems;所以如果我们一旦要使用这个Stack类的话,一样的,也可以用Stack<T> elem;来申明,当然T可以是int, float, std::string...如果是函数调用,那和普通类的调用一模一样。可以说这个实现还是波澜不惊的。

值得注意的一点是我们常常在成熟的类库中看到模板参数也是模板类的定义,例如:typedef Functor<Product*,Seq<int,int> > CreateFunctor;这个东西看似平淡,无非是这个Functor的第二个模板参数是Seq<int,int>而已,但是实际上,这个int>之后必须要有一个空格!没有这个空格,那连续两个大于号>>就被编译器误以为是>>操作符。但是,其实重点说的是但是!!!在VS2005中,编译器已经接受这样的定义,即使没有中间的空格,编译器还是可以正确理解的,只是如果你想写个在各个编译器下都可以跑的代码的话,还是建议你中间加上空格。 (编译器总是在改变,书却没有改变,跟不上时代也是很正常的事情 ^_^)

一般都是这样,在没有模板的时候,我们都期望有这样一个普遍成立的东西可以减少我们的工作,可真有了这个东西以后呢.又发现了这样的代码是针对所有的类型的,如果我需要对某些类型做定制呢.这个也有办法,这个办法就被称之为特化(Specialization).这里有一个Stack类为std::string做特化的例子.

#include "stack1.hpp"

template<>

class Stack<std::string> {

private:

std::deque<std::string> elems; // elements

public:

void push(std::string const&); // push element

void pop(); // pop element

std::string top() const; // return top element

bool empty() const { // return whether the stack is empty

return elems.empty();

}

};

void Stack<std::string>::push (std::string const& elem)

{

elems.push_back(elem); // append copy of passed elem

}

void Stack<std::string>::pop ()

{

if (elems.empty()) {

throw std::out_of_range

("Stack<std::string>::pop(): empty stack");

}

elems.pop_back(); // remove last element

}

std::string Stack<std::string>::top () const

{

if (elems.empty()) {

throw std::out_of_range

("Stack<std::string>::top(): empty stack");

}

return elems.back(); // return copy of last element

}

在这里用template<>class Stack<std::string> 这样一个头来表示对Stack类进行std::string类型的特化.在原文中有一句话,说的是如果你要特化一个类,那你必须特化所有的函数.当然,必须说明的是,必须特化你会用到的函数,如果这个top的函数以后不会使用.编译器也不会说你特化的不完整.其实如果你需要特化的范围比较小的话,直接特化一个函数也是可以的.比如你可以选择写成这样template<> void Stack<std::string>pop();注意,这里的template<>不写也是可以的.

到现在为止,我们见到的模板类的模板参数都只有一个, 当然我们可以有多个模板参数.这里先来看个刺激的:)

template

<

class AbstractProduct,

typename IdentifierType,

typename CreatorParmTList = NullType,

template<class> class EncapsulationPolicy = SimplePointer,

class CreationPolicy = AlwaysCreate,

template <typename , typename> class EvictionPolicy = EvictRandom,

class StatisticPolicy = NoStatisticPolicy,

template<typename, class> class FactoryErrorPolicy = DefaultFactoryError,

class ObjVector = std::vector<AbstractProduct*>

>

class CachedFactory :

protected EncapsulationPolicy<AbstractProduct>,

public CreationPolicy, public StatisticPolicy, EvictionPolicy< AbstractProduct * , unsigned >

这个是Loki里面一个寻常的定义,拿出来是想说明类模板是可以有多个模板参数的,同时就像普通的函数定义一样,也可以有默认参数.可能这个例子不是很合适,应该说这里不仅可以有参数,而且可以没有类型的模板参数.就是不是一个typename T类型的参数,比如说可以有个int的参数.像这样的定义也是可以的:template<typename T, int size>class Stack;这个size这里就可以留给 Stack类的使用者来决定(关键是编译器决定)这个Stack的大小. 当然了, int可以那short可以吗, long可以吗?原则上整数类型都是可以的,但是Float和Double直至今日还是不可以的.

回到特化上的事情上来,现在有了这么多参数,你还要特化岂不是很费力?没关系,C++ template还提供一个特性所谓偏特化( Partial Specialization)就是你可以针对一个或者几个模板参数进行特化而不是所有的参数.请原谅我的懒惰这就是一个偏特化的举例:

template<class T, class T2, class T3>class Stack

{

public:

void f1()

{

}

};

template<class T> class Stack<T, int, long>

{

public:

void f1()

{

}

};

当然啦,其实和特化差不多, 只不多是有选择性的而已.

好了,今天也差不多就说这么多吧,现在这个书看起来还是平平淡淡的,还看不出波澜.但是在深入的过程中还是慢慢会展现他的深邃,这个到和<<Modern C++ Design>>完全不同,不过也别有一番风味.