STL運用的C++技術(2)——模板特化

STL是C++標準庫的重要組成部分之一,它不僅是一個可複用的組件庫,更是一個包含算法與數據結構的軟件框架,同時也是C++泛型編程的很好例子。STL中運用了許多C++的高級技術。本文介紹模板特化技術的運用。主要參考了《C++ Primer》和《STL源碼剖析》。

       STL中大量運用了模塊,可以說模板是創建類或函數的公式。但是,我們並不總能寫出對所有可能被實例化的類型都最合適的模板。舉個函數模板特化的例子。

  1. template <typename T>  
  2. int Compare(const T &x, const T &y)  
  3. {  
  4.     if(x < y)  
  5.         return -1;  
  6.     else if(x > y)  
  7.         return 1;  
  8.     else  
  9.         return 0;  
  10. }  
         對於上面這個函數模板,如果用兩個字符串指針來調用,那麼比較的是指針值,也就是比較地址大小,而不是字符串的大小。因此爲了能夠將Compare函數用於字符串,就需要提供一個知道怎麼比較C風格字符串的特殊定義。這就是模板特化。

        模板特化(template specialization)的定義爲指定一個或多個模板形參的實際類型或實際值。例如可以爲Compare模板函數定義一個特化版本。

  1. template <> //template關鍵字後面接空括號  
  2. int Compare(const char * const &x, const char * const &y) //形參爲指向常量的常指針的引用  
  3. {  
  4.     return strcmp(x, y);  
  5. }  
        上文簡單闡述了模板特化,現在介紹模板特化在STL中的運用,以迭代器中的運用爲例。

        迭代器是STL的關鍵所在,它將原本分開的數據容器和算法很好的膠合在一起。比如下面這個STL中的函數(摘自源碼),命名上做了修改,同時略去了一些代碼,但是足以說明問題。這個函數通過迭代器交換容器的數據,迭代器是數據容器和算法的橋樑,算法通過數據容器的迭代器訪問容器中的數據,而不需關心容器的具體構造。

  1. //真正的交換函數,內部調用  
  2. template <class Iter1, class Iter2, class T>  
  3. inline void _iter_swap(Iter1 a, Iter2 b, T) {  
  4.   T tmp = *a;  
  5.   *a = *b;  
  6.   *b = tmp;  
  7. }  
  8. //交換兩個迭代器所指的元素,外部接口  
  9. template <class Iter1, class Iter2>  
  10. inline void iter_swap(Iter1 a, Iter2 b) {  
  11.  _iter_swap(a, b, VALUE_TYPE(Iter1)); //VALUE_TYPE返回迭代器的值類型  
  12. }  
       上面用到了一個VALUE_TYPE調用,註釋說是返回迭代器的值類型,具體如何下文會有介紹。舉這個例子,就是爲了引出這個調用。本文講的是模板特化,但是到這裏好像已經跑題了,不知所云。鋪墊差不多了,進入正題。

       問一個問題,iter_swap這個函數的形參是迭代器,我們需要在函數內部定義一個臨時變量,變量的數據類型爲迭代器所指的數據類型。那麼我們如何知道迭代器所指的數據類型呢?有人說,可以利用模板實參推斷機制,解決這個問題。代碼如下所示:

  1. //真正的交換函數  
  2. template <class Iter1, class Iter2, class T>  
  3. inline void _iter_swap(Iter1 a, Iter2 b, T) {  
  4.   T tmp = *a;  
  5.   *a = *b;  
  6.   *b = tmp;  
  7. }  
  8. //交換兩個迭代器所指的元素  
  9. template <class Iter1, class Iter2>  
  10. inline void iter_swap(Iter1 a, Iter2 b) {  
  11.  _iter_swap(a, b, *a); //模板實參推斷  
  12. }  
        但是如果要推導函數的返回類型,模板實參推斷機制就失效了。模板實參推斷機制的具體內容,將在本系列(3)中介紹。繼續上面的問題,本文用了一個稱之爲VALUE_TYPE的調用來獲取的,它就像是一個萃取劑,萃取出迭代器所指的數據類型。那麼它是如何實現的呢?答案就是內嵌型別。在STL中,大多數容器要求定義迭代器的內嵌型別,下面是 list 中的定義,已化簡。
  1. class MyAlloc{    
  2. };  
  3.   
  4. template<class T>  
  5. struct List_iterator{  
  6.   typedef T value_type;  //list 迭代器的內嵌型別  
  7.   ...  
  8. };  
  9.   
  10. template <class T, class Alloc = MyAlloc>  
  11. class list{  
  12. public:  
  13.   typedef List_iterator<T>  iterator;  //list迭代器  
  14.   ...  
  15. };  
        通過下面這種方式就可以萃取出 list 迭代器所指的數據類型。
  1. template<class I>  
  2. struct Iterator_traits{ //萃取劑定義  
  3.     typedef typename I::value_type value_type;  
  4. };  
  5.   
  6. Iterator_traits<list<int>::iterator>::value_type x = 1;  
        這種方式只能萃取出定義了內嵌型別的迭代器,但是如果是原生指針呢,它是沒有內嵌型別的?比如 vector 容器,它是用原生指針做迭代器的。定義如下:
  1. class MyAlloc{    
  2. };  
  3.   
  4. template <class T, class Alloc = MyAlloc>  
  5. class vector :   
  6. {  
  7. public:  
  8.   typedef T value_type;    //內嵌型別  
  9.   typedef value_type* pointer;  
  10.   typedef const value_type* const_pointer;  
  11.   typedef value_type* iterator;  //vector 迭代器,是原生指針  
  12.   typedef const value_type* const_iterator;  
  13.   typedef value_type& reference;  
  14.   typedef const value_type& const_reference;  
  15.   ...  
  16. };  
      模板特化終於登場了,下面加入了原生指針的支持,使用的正是模板特化技術,在泛化設計中加入了特化版本。該技術也是STL中的核心關鍵所在。
  1. template<class I>  
  2. struct Iterator_traits{  
  3.     typedef typename I::value_type value_type;  
  4. };  
  5. //特化 原生指針  
  6. template<class T>  
  7. struct Iterator_traits<T*>{  
  8.     typedef T value_type;  
  9. };  
  10. //特化 原生常指針  
  11. template<class T>  
  12. struct Iterator_traits<const T*>{  
  13.     typedef T value_type;  
  14. };  
    下面給出了完整的代碼,已在VS2008下測試通過。
  1. #include <iostream>  
  2. #include <vector>  
  3. #include <list>  
  4. using namespace std;  
  5.   
  6. //萃取劑  
  7. template<class I>  
  8. struct Iterator_traits{  
  9.     typedef typename I::value_type value_type;  
  10. };  
  11. //特化 原生指針  
  12. template<class T>  
  13. struct Iterator_traits<T*>{  
  14.     typedef T value_type;  
  15. };  
  16. //特化 原生常指針  
  17. template<class T>  
  18. struct Iterator_traits<const T*>{  
  19.     typedef T value_type;  
  20. };  
  21.   
  22. #define VALUE_TYPE(I) Iterator_traits<I>::value_type()  
  23.   
  24. //交換兩個迭代器所指的元素  
  25. template <class Iter1, class Iter2>  
  26. inline void iter_swap(Iter1 a, Iter2 b) {  
  27.  _iter_swap(a, b, VALUE_TYPE(Iter1)); //VALUE_TYPE返回迭代器的值類型  
  28. }  
  29. //真正的交換函數  
  30. template <class Iter1, class Iter2, class T>  
  31. inline void _iter_swap(Iter1 a, Iter2 b, T) {  
  32.   T tmp = *a;  
  33.   *a = *b;  
  34.   *b = tmp;  
  35. }  
  36. //測試函數  
  37. int main()  
  38. {  
  39.     int a = 1, b = 2;  
  40.     iter_swap(&a,&b);  
  41.     cout<<a<<' '<<b<<endl;  //2 1  
  42.       
  43.     list<int> l;  
  44.     l.push_back(3);  
  45.     l.push_back(4);  
  46.     iter_swap(l.begin(),++l.begin());  
  47.     cout<<*(l.begin())<<' '<<*(++l.begin())<<endl; //4 3  
  48.   
  49.     Iterator_traits<int *>::value_type w = 5;       //特化  
  50.     Iterator_traits<const int*>::value_type  x = 6; //特化  
  51.         Iterator_traits<vector<int>::iterator>::value_type y = 7; //vector 容器  
  52.     Iterator_traits<list<int>::iterator>::value_type z = 8;   //list 容器  
  53.     cout<<w<<' '<<x<<' '<<y<<' '<<z<<endl; //5 6 7 8  
  54.     return 0;  
  55. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章