traits技法小計

在學習算法導論的時候,對於各數據結構,天然是實現一個纔算掌握,工具固然是template編程,可是本身的demo常常存在不少問題,好比沒有給出迭代器啊,操做符重載
不夠啊等等設計上的問題,而某些問題其實是從設計之初就該考慮的大框架,而非小細節。對於C++而言,STL無疑是最佳的參考資料,侯捷先生的STL源碼剖析一書給咱們良好的示範,
而直接從第四章開始看會雲裏霧裏,沒法得其精髓,所以在學習算法之餘決定尾隨侯捷先生腳步,學習STL traits技法,從而能夠從STL中學到更多的數據結構實現。

收穫自是頗多,不只能夠掌握數據結構的基礎知識,還會教會咱們如何去篩選那些實現的方案,好比hash_table選擇了chaining法解決衝突,map和set採用紅黑樹等等,
並且對咱們C++泛型編程技法和C++重載技術也是極好的鍛鍊,源碼以前,滿是寶藏。
固然前提是有良好的數據結構知識和C++primer程度的C++技能。
本文對侯捷先生的第二章進行簡單歸納,便於往後查看,將一些和traits不太相關的好比C++語法等略去。


咱們以list的迭代器開發來逐步走入STL 迭代器的世界
  1 template<typename T>
  2 class List      //鏈表
  3 {
  4     void insert_front(T value);
  5     void insert_end(T value);
  6     void display(ostream &os=cout) const;
  7 private:
  8     ListItem<T>* _end;
  9     ListItem<T>* _front;
 10     long _size;
 11 };
 12 
 13 template<typename T>    
 14 class ListItem          //結點
 15 {
 16 public:
 17     T get_value()const;
 18     ListItem* next()const;
 19 private:
 20     T _value;
 21     ListItem* _next;
 22 };
 23 
 24 //要定義迭代器須要封裝指針
 25 template<class Item>  // Item的實際參數是ListItem
 26 struct ListIter
 27 {
 28     Item *ptr;     //指向結點的指針,迭代器就是在此基礎上封裝
 29 
 30     ListIter(Item *p=0):ptr(p){}//構造函數
 31 
 32     Item& operator*()const{return *ptr;}//解引用
 33     Item* operator->()const{return ptr;}//->操做符
 34     //...
 35 };
 36 
 37 /*
 38 這裏存在問題,咱們將listiter封裝,並於list綁定,用於泛型算法
 39 時,沒法獲取iter所指對象型別以及迭代器距離,所指之物的引用和指針等,
 40 STL中藉助模板類型推導來實現這一點
 41 */
 42 //第一想法是在模板形參表當中加入另外一個參數,即所指之物的類型,而後將雙參數的函數放在private中,在public接口函數中調用private函數
 43 
 44 template<typename Item, typename value>
 45 class Item
 46 {
 47 private:
 48     void func_pri(Item iter,value v);
 49 public:
 50     void func(Item iter){func_pri(iter,*iter);}
 51 };
 52 //問題又來了,若是在返回值中想要獲取類型怎麼辦?C++ 的參數推導不能夠在返回值中使用,由於從C繼承而來的函數的必要因素是參數和函數名,調用
 53 //一個函數並不須要返回值,所以若支持返回值自動推導,編譯器可能沒法獲知其類型
 54 
 55 這裏還有一種方法完成:
 56 template<class value>
 57 class Iter                    //    在迭代器的定義中內嵌一個關於value_type的聲明
 58 {
 59     typedef value value_type;
 60 };
 61 
 62 template<class I>
 63 typename Iter::value_type  func(I i)   //這是某個Iter適用的函數(泛型算法,並非Iter類的成員函數),算法的形參爲迭代器類型,在其中能夠訪問value_type,符合咱們的預期
 64 {
 65     return *i;
 66 }
 67 
 68 //只有一個小問題,和指針不兼容,由於指針不可內嵌聲明,由於它不是類類型。
 69 咱們想到了C++中template編程的一種技法,即部分特化,注意部分特化和特化是不一樣的,特化後實質已經不是模板,而部分特化是對模板的一個特殊狀況,還是模板
 70 接下來正式介紹traits技法
 71 
 72 -------------------------------------------------------------------------------------------------
 73 //trait技法
 74 template<class I>
 75 struct iterator_traits
 76 {
 77     typedef typename I::value_type value_type;
 78     typedef typename I::iterator_category iterator_category;
 79     /*此型別比較特殊,是由於iterator有5種(寫入、只讀、讀寫、雙向、隨機),應用於泛型算法時,不一樣算法針對不一樣類型的迭代器有不一樣的效率,應該分開實現
 80     好比,對於+操做而言,隨機迭代器效率是O(1),寫入是O(n),應該針對隨機迭代器另行實現
 81     讓咱們想到了函數重載,但問題是上面的不一樣類型的迭代器都是模板參數,編譯器沒法獲取它的類型,所以沒法重載,所以加入這一個參數做爲標誌,此標誌必須是類類型,才能夠推導進行重載決議
 82     具體的實現見P95*/
 83     typedef typename I::difference_type difference_type;
 84     typedef typename I::pointer pointer;
 85     typedef typename I::reference reference; 
 86 };
 87 部分特化版本--原生指針
 88 template<class P>
 89 struct iterator_traits<P*>
 90 {
 91     typedef typename P value_type;
 92     typedef typename random_access_iterator_tag iterator_category;
 93     typedef typename ptrdiff_t difference_type;
 94     typedef typename P* pointer;
 95     typedef typename P& reference; 
 96 };
 97 
 98 部分特化版本--const指針
 99 template<class P>
100 struct iterator_traits<const P*>
101 {
102     typedef typename P value_type;
103     typedef typename random_access_iterator_tag iterator_category;
104     typedef typename ptrdiff_t difference_type;
105     typedef typename const P* pointer;
106     typedef typename const P& reference; 
107 };
108 ------------------------------------------------------------------------------------------------------------------
109 
110 template<class I>
111 typename iterator_traits<I>::value_type  func(I i)  //這個和上面的無區別,就是間接取了一下,但好處在於有部分特化版本,對於不一樣類型的參數會返回正確的類型
112 {
113     return *i;
114 }
115 
116 
117 咱們本身實現iterator的時候,能夠繼承iterator class,見P100
相關文章
相關標籤/搜索