1.類模板顯式特化
為了進行特化,首先需要一個通用的版本,稱主模板.主模板使用了標準庫堆算法. 堆 是一種線性化的樹形結構,將一個值壓入一個堆中, 實際上等于將該值插入到一個樹形結構中;將一個值從堆中取出就等于移除并返回堆中最大值.但在處理字符的指針時會碰釘子.堆將按照指針的值進行組織. 我們可以提供一個顯式特化版本解決此問題(例1)如果希望除了一個針對const char*的Heap外,還希望提供一個針對char *的Heap;(例2)
//主模板template <typename T>class Heap{private: std::vector<T> h_;public: void push(const T& val); T pop(); bool empty() const //const聲明在末尾表示該函數不能修改類變量 { return h_.empty(); }}template <typename T>void Heap<T>::push(const T& val){ h_.push_back(val); std::push_heap(h_.begin(),h_.end());}template <typename T>T Head<T>::pop(){ std::pop_head(h_.begin(),h_.end()); T tmp(h_.back()); h_.pop_back(); return tmp;}
例1
//顯示特化版本/*********************************************** * 可以看到模板參數列表是空的,其實這根本不是一個模 * 板. 因為沒有指定任何模板參數.所以模板的顯式特化又被 * 稱作"完全特化". * Heap<const char*> 完全特化,不會導致模板的實例化; * Heap<int> 特化,會導致模板的實例化; * 編譯器根據主模板的聲明來檢查類模板特化.***********************************************/template<>//注意,無任何參數,當然,它本來就不是一個模板class Head<const char *>{private: std::vector<const char *> h_;public: void push(const char *pval); const char * pop(); bool empty() const //const聲明在末尾表示該函數不能修改類變量 { return h_.empty(); }};//再次提醒, Head<const char *>不是一個模板void Heap<const char*>::push(const char *pval){ h_.push_back(pval); std::push_heap(h_.begin(),h_.end());}
例2
/*********************************************** * C++沒有要求顯式特化的接口必須和主模板的接口完全 * 匹配.如該例中,沒有定義主模板的empty函數,并且自行增加 * 了size和capitalize兩個函數. * 提醒:此例中不定義empty函數是不可取的,定義模板的 * 顯式特化和類的派生之間雖然不存在任何技術上的聯系,但 * 是用戶依然可以參考類的派生的優點,讓特化版本至少具有 * 主模板的基本能力.***********************************************/template<>//注意,無任何參數,當然,它本來就不是一個模板class Head<char *>{private: std::vector<char *> h_;public: void push(char *pval); char * pop(); //注意,此處沒有提供empty函數喲!!! size_t size() const; void capitalize();};
2.模板局部特化
模板局部特化首先要聲明的是,C++還不支持對函數模板的局部特化,所以此處我們只討論類模板的局部特化.我們依然首先需要一個主模板.(參考類模板顯式特化) 自我理解:如果針對不能的指針定義不同的完全特化,豈不是太麻煩了,有沒有更好的辦法呢?那就是局部特化了.(例1)提示: 局部特化它是一個模板.完全特化不是一樣模板.
例1
/*********************************************** * 局部特化 * 和完全特化不同,這里的Heap參數類型只是被部分的確 * 定為T*,而T是一個未指定的類型,這就是為什么說它是局部 * 特化的原因; * 當使用一個未經任何修飾的指針類型來實例化Heap時, * 局部特化將優先于主模板; * 當使用const char * 或 char *(參考類模板顯式特化)來 * 實例化Heap時,此時完全特化又會優先于局部特化. * Heap<std::string> h1; 主模板 T是std::string * Heap<std::string *> h2; 局部特化 T是std:string * Heap<int **> h3; 局部特化 T是int * * Heap<char *> h4; 完全特化 T是char * * Heap<const int *> h5; 局部特化 T是const int * Heap<int (*)()> h6; 局部特化 T是int()***********************************************/template <typename T>class Heap<T *> //注意這里{private: std::vector<T *>h_;public: void push(const T *val); T *pop(); bool empty() { return h_.empty(); }};template <typename T>void Heap<T *>::push(const T *val){ //......}
例2
/*********************************************** * 有一點很微妙但很有用:主模板的完全特化或局部特化 * 必須采用與主模板相同數量和類型的實參進行實例化,但它 * 的模板的參數并不需要具有和主模板相同的形式.***********************************************///定義一個模板,有三個模板參數,書寫形式如下template <typename R,typename A1,typename A2> //注意,局部特化中,模板參數也是三個,但書寫形式可不一樣嘍class Heap<R (*) (A1,A2)>{ //......};Heap<char *(*) (int,int)> h7; //R是char *,A1和A2是int//把 char *(*) (int,int) 想象成一個"指向有兩個參數的非成員函數的指針"template <class C,typename T>class Heap<T C::*>{ //......};Heap<std::string Name::*> h8;//T是string,C是Name
盡管為何需要對這些東西使用Heap只是一個猜測,先知道有這么一用法吧!
3.類模板成員特化
雖然模板的特化和類的派生之間沒有任何關系, 但在特化模板的時候,不妨借鑒一下派生的精神.也就意味著一個完全特化或局部特化通常必須重新實現 主模板具備的所有能力.
例:
//主模板template <typename T>class Heap{private: std::vector<T> h_;public: void push(const T& val); T pop(); bool empty() const //const聲明在末尾表示該函數不能修改類變量 { return h_.empty(); }}//其實我們真正需要特化的是 push 和 pop兩個函數.//對比顯式特化,它是通過主模板,再寫一個模板顯式特化版本類;//而這里只是對類模板成員進行了單獨特化.template<>void Heap<const char*>::push(const char *const &pval){ h_.push_back(pval); std::push_heap(h_.begin(),h_.end(),strLess);}template<>const char* Heap<const char*>::pop(){ std:pop_heap(h_.begin(),h_end(),strLess); const char* tmp = h_.back(); h_.pop_back(); return tmp;}
注意,這些函數的接口必須和 "它們正在特化其成員" 的模板的相應接口相匹配.如例1, 就得和主模板的接口相匹配.而如果你是自己再定義的一個顯式/局部特化版本類,就不需要匹配 一致.(見顯式特化和局部特化),最后指出兩點: 首先,除了成員函數外,其實成員也可以被顯式特化,如靜態成員和成員模板.其次,顯式特化是為模板或模板成員提供定制版本的一種手段;而顯式實例化僅僅是明確地告訴編譯器去實例化一個成員.