使用traits技術表現迭代器類型 iterator_category

1.STL的迭代器類型標識,和容器中的迭代器類型。

2.使用trait技術實現stl的advance功能。

3.迭代器trait中的其他類型定義。 

 

1.STL的迭代器類型標識,和容器中的迭代器類型。

 熟悉的迭代器類型,輸入、輸出、前向、雙向、隨機。

c++標準程序庫分別提供專屬的卷標結構(tag struct),一個空的結構體來標誌具體是哪種類型迭代器。如下:

        //    ITERATOR STUFF (from <iterator>)
        // ITERATOR TAGS (from <iterator>)
struct input_iterator_tag
    {    // identifying tag for input iterators   
    };

struct output_iterator_tag
    {    // identifying tag for output iterators
    };

struct forward_iterator_tag
    : input_iterator_tag, output_iterator_tag
    {    // identifying tag for forward iterators  這些繼承關係有有效的IS-A
    };

struct bidirectional_iterator_tag
    : forward_iterator_tag
    {    // identifying tag for bidirectional iterators
    };

struct random_access_iterator_tag
    : bidirectional_iterator_tag
    {    // identifying tag for random-access iterators
    };

而在每個容器中,就使用上述的tag struct標識自自身迭代器的類型,大概的思路是下面的樣子,但真正源碼實現複雜多。

// vector 容器 隨機迭代器。list 容器 雙向迭代器。
template<...> class vector{ public: class iterator{ public: typedef random_access_iterator_tag iterator_category;
     // 類型定義: vector<T>::iterator::iterator_category 就是 random_access_iterator_tag
     // 即類型裏面還有一個類型, 而這個類型僅僅是用來標識 這個類是屬於哪個類型的。 }; }; template
<...> class list{ public: class iterator{ public: typedef bidirectional_iterator_tag iterator_category; }; };

 

2.使用trait技術實現stl的advance功能。

STL標準模版庫={容器templates,迭代器templates(關聯容器 和 算法),算法templates,工具性template如advance}。這裏選擇迭代器模版和Advance的實現來講解trait技術。

 

先看看advance的用法:

// advance 函數簽名
std::advance template
<class Iterator, class Distance> void advance (Iterator& it, Distance n);
// advance example
#include <iostream>     // std::cout
#include <iterator>     // std::advance
#include <list>         // std::list

int main () {
  std::list<int> mylist;
  for (int i=0; i<10; i++) mylist.push_back (i*10);

  std::list<int>::iterator it = mylist.begin();

  std::advance (it,5); 

  std::cout << "The sixth element in mylist is: " << *it << '\n';

  return 0;
}

advance內部操作時候,需要知道advance的迭代器類型,看有哪些可用操作,比如隨機訪問器可以直接+= -=操作,前向僅支持++,輸入輸出均不支持,例子中的list屬於雙向,支持++,--。

所以在advance內部需要在取得某種類型信息,即迭代器的類型,進行不同的實現。

 

如何取得類型信息呢,在1中其實我們已經定義了迭代器的類型,可以通過 ”容器類型::iterator::iterator_category“來獲取類型信息。

但如果要支持內置類型,比如指針是一種隨機迭代器類型,那麼類型信息就不能放在類型內了,意味着類型內的嵌套類的方式不能工作,所以類型的信息必須位於類型自身之外。

 

trait標準技術是把它放進一個template,並進行一個偏特化版本,來實現迭代器所屬類型trait。trait特性的意思 就有有關迭代器的相關特性。

// 即再封裝一層,如果是用戶自定義的,就直接獲取內部定義的迭代器類型
// 如果是內置類型,就直接給設定成他所屬的迭代器類型,用模版偏特化
// iterator_traits 
template<typename IterT> 
struct iterator_traits{ 
    typedef typename IterT::iterator_category iterator_category; 
    // 注意: 這裏typename關鍵字 指示編譯器解析 IterT::iterator_category 爲一個類型
    ... 
};

template<typename IterT>        //template 偏特化,針對內置指針 
struct iterator_traits<IterT*>{ 
    typedef random_access_iterator_tag iterator_category; 
};

template<typename IterT>       
struct iterator_traits<const IterT*>{ 
    typedef random_access_iterator_tag iterator_category; 
};
 

 

在advance的用法中,就可以使用 iterator_traits 類來判斷是什麼類型

// advance運行時確定使用哪種迭代器類型版本
template<typename IterT, typename DistT> void advance(IterT& iter, DistT d) { if (typeid(typename std::iterator_traits<IterT>::iterator_category) == typeid(std::random_access_iterator_tag)) { iter += d; } else if(前向迭代器類型) { if (d < 0){throw std::out_of_range("Negative distance");} while (d--) ++iter; } else if(等等其他類型) ... }

 

使用重載函數的機制,在編譯器就確定調用哪個迭代器類型的advance,以提高運行時效率。

// advance編譯器確定使用哪種迭代器類型版本
template<typename IterT, typename DistT> 
void advance(IterT& iter, DistT d) 
{ 
    doAdvance(iter, d, typename std::iterator_traits<IterT>::iterator_category()); 
}
// 比如 雙向迭代器的函數 。重載doAdvance,實現不同的迭代器類型的具體操作。
// 可以看到迭代器類型僅僅是個重載的作用,使得重載機制得以運行,都不需要變量名。
void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag)          
{                                                                                                               
    if (d >= 0) {while (d--) ++iter;} 
    else {while(d++) --iter;} 
} 

 

3.迭代器trait中的其他類型定義

 

 

template<class IterT>
struct iterator_traits{
    typedef typename IterT::iterator_category iterator_category;  // 迭代器的類型所屬
    typedef typename IterT::value_type value_type;            // 迭代器所指對象的類型
    typedef typename IterT::difference_type difference_type;     // 迭代器之間的距離
 
    typedef typename IterT::pointer pointer;              // 迭代器所指內容的地址
    typedef typename IterT::reference reference;            // 迭代器所指之內容
};

template<typename IterT>        //template 偏特化,針對內置指針 
struct iterator_traits<IterT*>{ 
    typedef random_access_iterator_tag iterator_category; 
    typedef IterT value_type; 
    typedef ptrdiff_t difference_type ; 
typedef IterT
* pointer ; typedef IterT& reference ; };

 

 

另外,STL提供了一個 iterator類,如果每個新設計的迭代器都繼承他,可保證符合STL所需的規範。

// TEMPLATE CLASS iterator
template<class _Category,
    class _Ty,
    class _Diff = ptrdiff_t,
    class _Pointer = _Ty *,
    class _Reference = _Ty&>
    struct iterator
    {    // base type for iterator classes
    typedef _Category iterator_category;
    typedef _Ty value_type;
    typedef _Diff difference_type;

    typedef _Pointer pointer;
    typedef _Reference reference;
 };

 

 

 

整理自 effective C++ 條款 47:使用traits classes表現類型信息

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