這一條款主要來討論模板中迭代器的屬性iterator_category,它能夠經過相似於vector<int>::iterator::iterator_category的方式來取得。dom
到這裏咱們有必要學習一下STL迭代器的類型,總共有五種,分別是:函數
input_iterator:只讀,只能逐個前移學習
output_iterator:只寫,只能逐個前移測試
forward_iterator:可讀可寫,只能逐個前移spa
bidirectional_iterator:可讀可寫,支持逐個前移和後移指針
random_access_iterator:可讀可寫,支持隨機訪問(任意步數移動)code
爲了代表容器內使用的是哪種迭代器,STL在定義迭代器會總會打個一個標記「tag」,每一個tag都是一個空的結構體,對應於以上五種迭代器,tag也有五個:對象
struct input_iterator_tag{};blog
struct output_iterator_tag{};繼承
struct forward_iterator_tag: public input_iterator_tag{};
bidirectional_iterator: public forward_iterator_tag{};
random_access_iterator: public bidirectional_iterator_tag{};
注意這五個tag之間,有些存在繼承關係。
這個標記有什麼用呢?STL在寫vector的時候,會這樣:
1 template class <T> 2 class vector 3 { 4 public: 5 class iterator 6 { 7 public: 8 typedef random_access_iterator iterator_category; 9 … 10 } 11 … 12 } 13 14 寫list的時候,會這樣寫: 15 template class <T> 16 class list 17 { 18 public: 19 class iterator 20 { 21 public: 22 typedef bidirectional_iterator iterator_category; 23 … 24 } 25 … 26 }
既然迭代器已經由tag說明了它的類型(雙向的,仍是隨機訪問),那咱們如何去利用它呢?好比如今我想要寫一個迭代器前移的通用函數DoAdvance,不一樣迭代器類型會有不一樣的實現方式,因此咱們能夠像下面這樣:
1 template <class T> 2 void DoAdvance(T Container) 3 { 4 typedef T::iterator::iterator_category IteratorCategory; 5 if (typeid(IteratorCategory) == typeid(input_iterator_tag)) 6 { 7 cout << "Do manner in input_iterator_tag" << endl; 8 } 9 else if (typeid(IteratorCategory) == typeid(output_iterator_tag)) 10 { 11 cout << "Do manner in output_iterator_tag" << endl; 12 } 13 else if (typeid(IteratorCategory) == typeid(forward_iterator_tag)) 14 { 15 cout << "Do manner in forward_iterator_tag" << endl; 16 } 17 else if (typeid(IteratorCategory) == typeid(bidirectional_iterator_tag)) 18 { 19 cout << "Do manner in bidirectional_iterator_tag" << endl; 20 } 21 else if (typeid(IteratorCategory) == typeid(random_access_iterator_tag)) 22 { 23 cout << "Do manner in random_access_iterator_tag" << endl; 24 } 25 }
參數T是容器的類型,好比vector<int>,若是像下面這樣調用:
1 vector<int> v; 2 DoAdvance(v);
那麼輸出是Do manner in random_access_iterator_tag,由於vector<int>的迭代器是隨機訪問型的,能夠按隨機訪問類型的處理方式來去實現前移操做。typeid返回結果是名爲type_info的標準庫類型的對象的引用,它指明瞭這個對象/定義的類型。
由於這裏討論的是迭代器,因此更常見的是直接傳迭代器進去,像這樣:
1 template <class IterT> 2 void DoAdvance(IterT Iter) 3 { 4 typedef IterT::iterator_category IteratorCategory; 5 if (typeid(IteratorCategory) == typeid(input_iterator_tag)) 6 { 7 cout << "Do manner in input_iterator_tag" << endl; 8 } 9 … 10 }
注意這裏的模板參數是IterT,它表示一個迭代器的類型,好比vector<int>::iterator。這裏是去主動訪問iterator裏面定義的屬性iterator_category,咱們也能夠經過trait classes來訪問,像下面這樣:
1 template <class IterT> 2 void DoAdvance(IterT Iter) 3 { 4 if (typeid(iterator_traits<IterT>::iterator_category) 5 == typeid(input_iterator_tag)) 6 { 7 cout << "Do manner in input_iterator_tag" << endl; 8 } 9 … 10 }
iterator_traits的定義以下:
1 template<class IterT> 2 struct iterator_traits<IterT> 3 { 4 typedef typename IterT::iterator_category iterator_category; 5 … 6 };
這個感受只是簡化了輸入代碼量而已,本質上仍是去得到迭代器的tag,它有一個針對指針的偏特化版本,像下面這樣:
1 template<class IterT> 2 struct iterator_traits<IterT*> 3 { 4 typedef random_access_iterator_tag iterator_category; 5 };
這裏都是用typeid去進行類型判斷的,它是在運行期才能執行,那麼能不能放在編譯期呢,固然能夠,就是要用到函數的重載,像下面這樣:
1 template <class IterT> 2 void DoAdvance(IterT Iter, input_iterator_tag) 3 { 4 cout << "Do manner in input_iterator_tag" << endl; 5 } 6 7 8 template <class IterT> 9 void DoAdvance(IterT Iter, random_access_iterator_tag) 10 { 11 cout << "Do manner in random_access_iterator_tag" << endl; 12 }
像下面這樣使用;
1 vector<int>::iterator iter; 2 DoAdvance(iter, iterator_traits<vector<int>::iterator>::iterator_category());
注意迭代器的tag是能夠直接做爲函數形參的,這樣就能夠在編譯期決定到底執行哪種迭代器的行爲了。
條款標題的traint classes是一個廣義的概念,咱們以前討論的iterator_traits只是其一部分,除以以外,還有四份迭代器相關的信息(如value_type等),TR1導入許多新的trait classes,好比is_fundamental<T>等(判斷T是不是內置類型)。
最後,咱們來總結一下:
1. Traits class使得類型相關信息能夠在編譯期可用,它們以template和template特化完成實現;
2. 整合重載技術後,traits classes有可能在編譯期對類型執行if-else測試。