籃子、水果和雞蛋——關於C++的模板偏特化和萃取編程技法

最近在讀《STL源碼剖析》。讀這本書的時候發現本身的C++的知識實際上是很是匱乏的。 從大學的C++教材上學到一些C++基本的語法、內存管理、繼承、多態等方面的基礎知識。這些只是是一棵大樹的根。而讀STL的源碼和侯捷的解析的時候,發現C++還有不少豐富的細節和技巧。這些是大樹上的枝葉。學習C++,不只要學習根,也要學習枝葉,這樣才能讓大樹茂盛起來。雖然C++語法一些用法較爲晦澀,但讀完這些代碼以後以爲思路比之前更開闊,另外能夠活動腦筋。 編程

好比模板的偏特化這個特性。侯捷的《STL源碼剖析》中對於模板的偏特化(partial specialization)的解釋爲: 若是class template擁有一個以上的template參數,咱們能夠針對其中某個或多個 template參數進行特化工做。template是一個很抽象的東西。template偏特化以後就讓模板變得具體那麼一點點。 函數

用一個形象一點的比喻吧。咱們把template比做一個裝東西的籃子。這個籃子既能夠裝雞蛋,也能夠裝蘋果。那麼所謂偏特化就是讓你用一個籃子專門裝水果,這就是template水果籃。之後你就只能使用水果籃來裝蘋果、裝梨,而不能使用其餘的籃子來裝這些水果了。 學習

看一個STL例子: 設計

有一個「籃子」 iterator_traits, 它內部typedef 了value_type類型,用來定義模板的參數類型class I 的value_type。 指針

template<class I>
struct iterator_traits
{
    typedef typename I::value_type value_type;
};
若是用通常的STL的iterator類型去實例化這個模板的時候是不會有問題的,由於STL內建的iterator裏typedef了value_type。可是若是用原生指針去實例化這個模板就悲劇了。由於原生指針可沒有辦法typedef什麼東西。若是把iterator比做雞蛋,把原生指針比做水果,那麼這個籃子就只能裝雞蛋而不能裝水果。若是必需要裝水果,怎麼辦?


辦法就是用模板的偏特化,給水果專門作一個水果籃了。看下面的代碼 code

template <class T>
struct iterator_traits<T*>
{
typedef T value_type;
}

當用原生指針T*去實例化iterator_traits模板的時候,就會調用上面的代碼把類型T typedef爲value_type。這就至關於給iterator_traits實現了一個特別的版本專門處理原生指針。也就是說,水果籃作好了:) 對象

若是理解了模板的偏特化, 就理解了萃取編程技法的一大半了。什麼是萃取編程技法呢? 萃取來自於英語的traits, traits的意思是特性、特色。萃取編程技法實際上就是利用模板的偏特化,充分的萃取不一樣型別的特色。 繼承

仍是拿雞蛋、水果和籃子作例子。別看裝雞蛋、水果都用籃子,可是裝雞蛋的籃子和裝水果的籃子能同樣嗎? 裝雞蛋的籃子必定要抗摔防震。由於雞蛋殼很脆一碰就破。而裝水果的籃子則要美觀好看。由於水果籃通常都用做送給別人的禮物。 內存

仍是用上面的iterator_traits 這個籃子作例子,看下面的代碼。 ci

template<class I>
struct iterator_traits
{
    typedef typename I::value_type value_type;
}; //泛化版本

template<class I>
struct iterator_traits<T*>
{
    typedef T value_type;
}; //偏特化版本

咱們給這個iterator_traits設計了兩個版本。上面的是泛化的版本,下面的是爲原生指針偏特化的版本。

如何使用iterator_traits這個籃子呢,看下面這個模板函數:

template <class I>
typename iterator_traits<I>::value_type 
func(I ite)
{
    return *ite;
}

這個模板函數以一個class I做爲類型參數, 函數的參數爲一個class I類型的對象, 返回的是class I對象提領的對象。函數的返回值類型定義就使用了萃取編程的技法。咱們看函數返回值的定義:

iterator_traits<I>

這個模板就是上文中咱們定義的籃子。在上文中咱們針對不一樣類型的特色,實現了兩種將template的實例化的方法:實現了用STL的iterator去實例化這個模板的方法,也實現了用原生指針去實例化這個模板的方法。由於咱們「因材施教」的調用了不一樣的代碼,才把水果放在了水果籃裏,把雞蛋放在其餘的籃子裏。這就是萃取的編程技法。


C++的語法是很艱深的。STL對於C++的使用也能夠說是頗有「創造性」。 由於這種「創造性」,給咱們閱讀STL源碼的時候形成了必定的困難,不過也是由於這種「創造性」 , 讓咱們使用STL的時候很是的輕鬆和愉快。哎,這就是所謂的「痛而且快樂着」的那種感受吧。

相關文章
相關標籤/搜索