這一條款主要來討論模板中迭代器的屬性iterator_category,它可以通過類似於vector::iterator::iterator_category的方式來取得。
到這裡我們有必要學習一下stl迭代器的型別,總共有五種,分別是:
input_iterator:唯讀,只能逐個前移
output_iterator:只寫,只能逐個前移
forward_iterator:可讀可寫,只能逐個前移
bidirectional_iterator:可讀可寫,支援逐個前移和後移
random_access_iterator:可讀可寫,支援隨機訪問(任意步數移動)
為了表明容器內使用的是哪一種迭代器,stl在定義迭代器會總會打個乙個標記「tag」,每個tag都是乙個空的結構體,對應於以上五種迭代器,tag也有五個:
struct input_iterator_tag{};
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 class2class
vector311
…12}13
14寫list的時候,會這樣寫:
15 template class
16class
list
1725
…26 }
既然迭代器已經由tag說明了它的型別(雙向的,還是隨機訪問),那我們如何去利用它呢?比如現在我想要寫乙個迭代器前移的通用函式doadvance,不同迭代器型別會有不同的實現方式,所以我們可以像下面這樣:
1 template2void
doadvance(t container)39
else
if (typeid(iteratorcategory) ==typeid(output_iterator_tag))
1013
else
if (typeid(iteratorcategory) ==typeid(forward_iterator_tag))
1417
else
if (typeid(iteratorcategory) ==typeid(bidirectional_iterator_tag))
1821
else
if (typeid(iteratorcategory) ==typeid(random_access_iterator_tag))
2225 }
引數t是容器的型別,比如vector,如果像下面這樣呼叫:
1 vectorv;2 doadvance(v);
那麼輸出是do manner in random_access_iterator_tag,因為vector的迭代器是隨機訪問型的,可以按隨機訪問型別的處理方式來去實現前移操作。typeid返回結果是名為type_info的標準庫型別的物件的引用,它指明了這個物件/定義的型別。
因為這裡討論的是迭代器,所以更常見的是直接傳迭代器進去,像這樣:
1 template2void
doadvance(itert iter)39
…10 }
注意這裡的模板引數是itert,它表示乙個迭代器的型別,比如vector::iterator。這裡是去主動訪問iterator裡面定義的屬性iterator_category,我們也可以通過trait classes來訪問,像下面這樣:
1 template2void
doadvance(itert iter)39
…10 }
iterator_traits的定義如下:
1 template2struct iterator_traits3
;
這個感覺只是簡化了輸入**量而已,本質上還是去獲得迭代器的tag,它有乙個針對指標的偏特化版本,像下面這樣:
1 template2struct iterator_traits3
;
這裡都是用typeid去進行型別判斷的,它是在執行期才能執行,那麼能不能放在編譯期呢,當然可以,就是要用到函式的過載,像下面這樣:
1 template2void
doadvance(itert iter, input_iterator_tag)36
78 template
9void
doadvance(itert iter, random_access_iterator_tag)
10
像下面這樣使用;
1 vector::iterator iter;2 doadvance(iter, iterator_traitsint>::iterator>::iterator_category());
注意迭代器的tag是可以直接作為函式形參的,這樣就可以在編譯期決定到底執行哪一種迭代器的行為了。
條款標題的traint classes是乙個廣義的概念,我們之前討論的iterator_traits只是其一部分,除以之外,還有四份迭代器相關的資訊(如value_type等),tr1匯入許多新的trait classes,比如is_fundamental等(判斷t是否是內建型別)。
最後,我們來總結一下:
1. traits class使得型別相關資訊可以在編譯期可用,它們以template和template特化完成實現;
2. 整合過載技術後,traits classes有可能在編譯期對型別執行if-else測試。
《effective C 》讀書筆記
1,c 關鍵字explicit c 中,乙個引數的 建構函式 或者除了第乙個引數外其餘引數都有預設值的多參建構函式 承擔了兩個角色。1 是個 構造器,2 是個預設且隱含的型別轉換操作符 所以,有時候在我們寫下如 aaa 這樣的 且恰好 的型別正好是aaa單引數構造器的引數型別,這時候 編譯器就自動呼...
Effective C 讀書筆記
一 讓自己習慣c 1 條款01 視c 為聯邦語言 c 的組成可分為四部分 1.c c 仍然以c語言為基礎。區塊 語句 預處理 內建資料型別 陣列 指標等都來自c。2.object oriented c c with classes所訴說的 classes 包括構造和析構 封裝 繼承 多型 virtu...
讀書筆記 Effective C
部分條款過於深奧,部分條款已了然於心,僅記錄當下所識所學 對於常量巨集定義,最好用const代替 define 對於函式巨集定義,最好用inline代替 define include ifdef ifndef仍被需要 內建物件記得手動初始化 使用成員初始列替換賦值操作 以local static替換...