我們在上一篇文章中為大家詳細介紹了C++類模板的具體應用方式,相比朋友們應該可以從中對類模板有一個充份的認識。那麼C++類模板特化又是如何進行的呢?其實,C++類模板特化被描述為一個和重載類似的概念。
我的理解是,特化允許我們對某些特殊的參數這裡就是類型)進行特殊的處理。C++類模板特化的處理都是在類名後面做文章的。特化分為全局特化和局部特化。對於全局特化,書中的例子是希望對於Stack< T>模板,如果參數為std::string的類型,就用deque作為容器來處理,而其他的保持不變。因此,我們需要對Stack< T>模板作std::string的特化處理。代碼如下:
- #include < deque>
- #include < string>
- #include < stdexcept>
- #include "Stack.h"
- template< >
- class Stack< std::string>{
- private:
- std::deque< std::string> elems;
- public:
- void push(std::string const&);
- void pop();
- std::string top() const;
- bool empty() const{
- return elems.empty();
- }
- };
- void Stack< std::string>::push(std::string const& elem)
- {
- elems.push_back(elem);
- }
- void Stack< std::string>::pop()
- {
- if(elems.empty())
- {
- throw std::out_of_range("Stack< std::string>::pop()==> empty stack.");
- }
- elems.pop_back();
- }
- std::string Stack< std::string>::top() const
- {
- if(elems.empty())
- {
- throw std::out_of_range("Stack< std::string>::pop()==> empty stack.");
- }
- return elems.back();
- }
注意到C++類模板特化的定義和普通的類模板完全不一樣了。主要區別有:
特化類模板的前面加上了template< >,沒有指定參數。而是在類名後面指定了類型參數。
在函數的定義裡面,原來的類型T全部換成了特化的類型std::string。實際上,完全可以根據特殊需要重寫成員函數。甚至可以定義另外的函數。
將上面的源代碼加入到工程中,編譯運行。就會發現當使用std::string去實例化stack的時候實際上調用的是StringStack文件中的"重載"版本。各個方法的調用也一樣。也就是說,特化實際上是要求對特定的參數施行特殊的處理。從這個方面來說和重載確實很類似。
但是,我認為特征化和重載還是有區別的。試想有一個函數Func(int, int),另外一個函數對它進行重載為Func(string, string)。在實際上我們也可以說int的Func重載了string的Func,這是相互的。但是特化卻不能這麼說。因為特化是對某種類型的特殊處理,我們可以說特化模板重載了某個模板,但是不能說某個模板重載了特化的模板。這是單方向的。另外,如果,我們不需要Func(int, int)函數,我們完全可以把它刪去。但是C++類模板特化不能離開它依賴的類模板單獨存在。在上面的例子中,如果刪除Stack.h文件,StringStack.h文件的定義就會出錯。
StringStack是Stack模板的特化。但是他們之間的聯系其實不是那麼緊密,除了名字上以外。例如,Stack模板中的成員函數不必非得在StringStack中出現;同理,StringStack中的函數也不必是Stack中的函數。也就是說,特化的模板類可以根據自己的需要完全重寫指定的模板函數,也可以棄原來模板函數中的成員不用,另外定義成員函數。這方面沒有限制。
在理解了全局的特化以後,在來看局部的特化就很容易明白了。局部特化是要求在指定的條件下使用指定的類模板的重載版本。