首页 > 代码库 > C++必知必会(5)

C++必知必会(5)

条款47模板局部特化

不能对函数模板进行局部特化,所能做的即使重载它们。

但可以对类模板进行局部特化。

template<typename T> class Heap;      //主模板

template<typename T> classHeap<T*>{…}; //局部特化

局部特化的语法类似完全特化,但是他的模板参数列表是非空的。当使用任何(未经修饰的)指针类型来实例化一个Heap时,这个局部特化版将优先于主模板而被采用。进一步而言,当模板实参类型为const char*或char*时,针对const char*和char*的完全特化版本的Heap又将优先于这个局部特化而被采用。

       主模板的完全特化或局部特化必须采用与主模板相同数量和类型的实参进行实例化,但它的模板参数列表并不需要具有和主模板相同的形式。对于Heap来说,主模板带有单个类型名字参数,因此Heap的任何完全特化或局部特化都必须通过采用单个类型名字实参来实例化。

template<>class Heap<const char*>{…};    //完全特化,带有单个类型名字模板

//实参,但模板参数列表不同于主模板参数列表,完全特化的模板参数是空的

template<typenameT> classHeap<T*>{…}; //局部特化,带有单个类型实参,模板参//数列表也可以带有单个类型名字参数,但不是必须的,如下 

template<typenameT,int n> classHeap<T[n]>{…};    //局部特化,带有单个类型实参,    但该参数必须是一个具有n个类型为T的元素的数组

条款48类模板成员特化

对主模板而言,类模板的完全特化和局部特化全然是单独的实体,他们不从主模板“继承”任何接口或实现。但通常期望能有和主模板一套相同的接口。

template<typenameT>

class Heap

{

public:

       void push(const T& val);

};

可以不针对模板进行特化,只针对模板成员函数特化。

template<>

void Heap<const char*>::push(constchar* const &pval){…}

注意,这些函数的接口必须和进行成员特化的模板对的相应接口精确匹配。例如,主模板将push声明为带有一个类型为const T &的参数,因此针对const char*的push显式特化的实参必须为const char* const &。

如果已存在针对一般指针的Heap局部特化,

template<typenameT>

class Heap<T*>

{

void push(T*pval);

};

如果这个局部特化已经存在,那么对push的显式特化就必须符合该局部特化中push成员接口,因为该函数相当于针对Heap<const char*>进行实例化所得的结果。所以,显式特化现在必须声明为:

template<>

void Heap<constchar*>::push(const char* pval){…}

条款49利用typename消除歧义

使用tepename关键字可以明确地告诉编译器,接下来的限定名字是一个类型名字,从而允许编译器去正确的解析模板。这个关键字经常被用在说明嵌套类型中。

模板50成员模板

一个成员模板就是一个自身是模板的成员。

template <typename T>

class Slist{

public:

       //…

       template<typenameIn> Slist(In begin, In end);

};

 

template<typename T>

template<typename In>

SList<T>::SList(In begin, In end) :head_(0){

       while(begin!= end)

              push_front(*begin++);

       reverse();

}

条款51 采用template消除歧义

std配置器中的rebind是一个模板,当如下定义式将会出错:

template<typename T, class A =std::allocator<T>>

class SList{

       //…

       structNode{

       //…

};

typedefA::rebind<Node>::other NodeAlloc;       //语法错误!

       //…

};

解决办法:

typedef typename A::template rebind<Node>::otherNodeAlloc;

使用关键字template,即等于告诉编译器,rebind是一个模板,而使用typename则等于告诉编译器,整个这一堆东西表示的是一个类型名字。

条款52针对类型信息的特化

可以从一个特化版本中推导出类型的属性。让我们看一个简单的例子:

template <typename T>

struct IsInt    //T表示一个int

{

       enum{result = false};

};

 

template<>

struct IsInt<int>   //除非是一个int

{

       enum{result= true};

};

有了这个主模板和完全特化版本,就可以在编译器询问一个未知的类型是否确实为int:如下

template<typenameX>

void aFunc(X&arg){

       //…

       …IsInt<X>::result…

       //…

}

条款53 嵌入的类型信息

template<class T>

class Seq{

public:

       typenameT Elem;             //元素类型

       typenameT Temp;            //临时对象的类型

       size_tsize()  const;           

       //…

};

在编译期可以查询到这种嵌入的信息:

typedef Seq<std::string> Strings;

//…

Strings::Elem aString;

条款54traits

见http://blog.csdn.net/walkerkalr/article/details/17287939条款47:请使用traitsclasses表现类型信息