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表現類型信息