讀書筆記_Effective_C++_條款四十七:請使用trait classes來表示類型信息

這一條款主要來討論模板中迭代器的屬性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測試。

相關文章
相關標籤/搜索