STL中×××源碼下載實現 iterator trail 的編程技巧
阿新 • • 發佈:2018-07-23
想是 rim void 指針類型 constant 偏特化 empty 思考 infer 《泛型編程和 STL》筆記及思考。 這篇文章主要記錄在 STL 中叠代器設計過程中出現的編程技巧,圍繞的 STL 主題為 (叠代器特征) Iterator traits 和 相關類型(Associated Types)。 首先介紹 Associated Types Associated Types 我們知道,Iterator 是一種泛化的指針,我們有時會這樣理解它: 指針(廣義的)指向某個序列的一個 item,而每個 item 的類型就是我們想要的 Associated Types。 對於 C 中的指針來說,ptr 就是他的相關類型。 對於我們之前定義的外覆類(C++ 泛型算法 find 那篇)來說, int_node (即 Node)是他的相關類型。 現在我們有個問題,我們如何使用這個類型?假設有一個 Iterator type P,P 的相關類型為 value_type。我們想要在一些算法中使用這個×××源碼下載dashengba.com【大聖源碼論壇】企娥3266397597【蘋果源碼論壇】bbsapple.com類型聲明一些臨時變量(這很常見,比如在計算數值的算法內),然而 ”P 的 value_type “ 並沒有告訴我們 C++ 如何表達這樣的概念。比如在之前的外覆類中,我們可以通過外覆類的接口獲得他的相關類型的具體引用,但是卻沒辦法使用這個類型信息用於外覆類之外的變量的聲明。 技巧一 :使用 C++ 的類型推斷機制 假設有個泛型函數 f() ,需要一個類型為 P 的參數,而且還需要聲明一個類型為 P 的 value_type 的臨時變量。我們可以令 f() 成為一個單純的傳遞函數,並將所有工作委托給內部函數 f_impl(): template <class P,class T> void f_impl(P iter,T t) { T tmp; ... } template <class T> inline void f(T iter) { f_impl(iter,iter); } 於是,我們可以在函數 f_impl 之中,聲明一個類型為 T 也即 P 的 value_type 類型的臨時變量。template 參數的 “type inference” 機制是自動自發的,所以在 f_impl 之中,template 的參數 T 和 iter 是同義的。 這個技巧的確能夠解決我們之前提出的問題,但是,他卻解決不了另一個問題,這個方案無法用來聲明函數的返回類型。通過類型推導得到的 Associated Type 信息是在 f() 方法內獲得的,我們無法將這個 Associated Type 用於函數的返回值。所以我們來看一下技巧二。 技巧二 : 嵌套聲明 這種方法的核心思想是在每個叠代器類中將具體的 Associated Type 統一定義為一個新的名為value_type(又一次的隱藏了差異性)的類型。 用上一篇定義的外覆類 int_node 為例,為它增加一個新的類型 value_type: template <class Node> //Node 是類型參數,可能用 T 更好理解 struct node_wrap { typedef Node value_type; Node ptr; ... }; 這樣的話,只要我們知道這個叠代器的類名,便可以訪問他的 Associated Type 。並且,在每個定義了 value_type 的叠代器都可以換通過相同的方法訪問。 對於聲明一個 node_wrap 的 Associated Type 類型的變量,我們可以這樣: typename node_wrap :: value_type tmp; 以這種方式來對變量命名。(typename 是為了向編譯器指出這是類型名而不是成員方法名或其他) 聲明函數的返回類型: typename node_wrap:: value_type f(...) { ... } 這樣做相當於我們再次將差異性隱藏,向外提供了一個統一的接口。 看起來這種方式解決了我們的問題。但是我們忽略了一些情況。上述成立的前提是叠代器是一個類,所以我們可以在它們內部增加一個新的類型,並從外部訪問它。但是 C 中的指針也是一個叠代器,而且我們一直是在以 C 指針的行為來指導叠代器的行為。顯然我們無法為 C 指針提供這種操作。為了解決這個問題,為指針也提供合適的操作,我們再次提出了新的方案。 技巧三 : 增加中間層和模板偏特化 我們可以增加一個中間層來解決這個問題。具體的做法是定義一個輔助用的 class,iterator_traits: template <class Iterator> struct iterator_traits { typedef typename Iterator::value_type value_type; }; 這麽一來我們就可以以這樣的寫法來取用 Iterator 的 value_type(以聲明變量為例): typename iterator_traits<node_wrap>::value_type tmp; 這看起來沒有任何改進,因為 Iterator_traits 仍然假設它的 template 參數 Iterator 有一個嵌套類型。(指針仍然沒有) 不過,現在我們可以使用另一種技術來解決這個問題了。我們將利用 “對某種 template 參數提供另一種定義” 的方法,將 Iterator_traits class 特化。 C++ 中允許 template 的全特化(亦即為某型別如 int 提供另一種定義)和偏特化(亦即提供另一個定義,其自身仍為模板化的。具體可參考 《C++ primer》)。在這個例子中,我們需要針對每一種指針類型做出另一種定義,因此我們需要偏特化: template <class T> struct iterator_traits<T> { typedef T value_type; }; 現在,當我們面對普通的指針時,也可以像對待叠代器一樣,使用相同的方法來取用它的 Associated Type。 主要的問題已經解決了,但是還有一個細節問題需要提出來討論一下:constant iterator 的 value_type 是什麽?考慮如下例子: iterator_traits<const int>::value_type 根據我們之前定義的 Iterator_traits value_type 將會是 const int 而非 int。我們已經針對 T 的參數將 iterator_traits 特化,但當 T 是 const int 時,const int 會符合 T ,這不是我們期待的結果。 我們只需要另外設計一個版本的偏特化,就能輕而易舉的解決這個問題: template<class T> struct iterator_traits<cosnt T> { typedef T value_type; }; 現在我們有了三種不同的 itertaor_traits 版本。一個是針對類型為 T 的參數,一個是 T ,另一個是 const T 的參數。當你寫下 iterator_traits<const int> 時,原則上他符合任何一個版本,不會有歧義的情況發生。因為 C++ 編譯器總能選出最能明確匹配的版本。 直到到現在,我們有了某種機制,讓我們得以使用某個 iterator 的 value_type 來撰寫算法。 舉個例子,以下的泛型函數用來計算 nonempty range 內的數值總和: template <class InputIterator> typename iterator_traits<InputIterator>::value_type sum_nonempty(InputIterator first,InputIterator last) { typename iterator_traits<InputIterator>::value_type result = first; for (;first != last; ++first) result += *first; return result; }
STL中×××源碼下載實現 iterator trail 的編程技巧