STL原始碼剖析(三)迭代器與traits程式設計
阿新 • • 發佈:2018-11-19
文章目錄
1. 迭代器概念
1.1 基本概念
- 一種抽象的設計概念。在《Design Patterns》中有如下定義:
提供一種方法,使之能夠依序巡訪某個聚合物所含的各個元素,而又無需暴露該聚合物的內部表述方式
- 簡單來說,迭代器可以讓我們訪問容器內部的元素,而我們又不需要知道內部是如何構造的
- 迭代器將容器與演算法撮合在一起
- 迭代器最重要的工作就是對operator* 和operator->進行過載
1.2 迭代器設計理念
- 設計一個容器的迭代器,首先需要對容器的構造非常瞭解,就將迭代器的開發工作交給容器的設計者,這樣就能將所有實現細節封裝起來不被使用者看到
2. 引出traits程式設計
triats,譯為特性,traits程式設計即為特徵程式設計
- 由推導迭代器相應型別引出traits程式設計
- 利用
function template
的引數推導機制得出迭代器所指物件的型別
template <class I, class T>
void func_impl(I iter, T t)
{
T tmp;
//....
};
template <class I>
inline
void func(I iter)
{
func_impl(iter, *iter);
}
int main()
{
int i;
func(&i); //呼叫模板,自動進行引數推導,匯出型別T
}
- 此方法有一定的侷限性,對於函式的返回值型別無法進行推導
- 宣告
內嵌型別
,利用該型別得出返回值型別
template <class T>
struct MyIter {
typedef T value_type;
T* ptr;
MyIter(T* p = 0) : ptr(p) {}
T& oeprator*() const { return *ptr; }
//...
};
template <class I>
typename I::value_type //一整行作為func的返回值型別,typename告訴編譯器這是一個型別
func(I ite)
{
return *ite;
}
//....
MyIter<int> ite(new int(8));
cout << func(ite);
- 此方法亦有一定的侷限性,即對於不是class type的迭代器而言,就無內嵌型別,亦無法推導
- 能不能找尋一種可以針對某種特定情況做出特定處理的方案呢?答案是可以的,template partial specialization->模板特例化可以做到
3. traits程式設計
3.1 traits程式設計技術
- traits程式設計技術就是利用內嵌型別以及通過模板的型別推導機制,獲得變數的型別,而後決定dispatch到哪個函式
- 接下來首先得到迭代器的型別:
3.2 partial specialization(偏特化)
- 假設定義了一個類模板C:
template <typename T>
class C {...}; //允許接受T為任意型別
- 那麼所謂的偏特化即指對template引數進行進一步的約束,但其本質仍然是模板:
template <typename T>
class C<T*> { .... }; //這個特化版本僅適用於“T為原生指標的情況
//”T為原生指標”即是對模板引數的再一次約束
3.3 traits
- 專門用來"萃取"迭代器的特性的一個模板類,可根據迭代器的型別不同進行特殊的特化:
template <class I>
struct iterator_traits {
typedef typename I::value_type value_type;
};
//迭代器是一個原生指標時
template <class T>
struct iterator_traits<T*> { //偏特化版,迭代器是個原生指標
typedef T value_type;
};
- 通過上述的方式進行構造,對於每一個特殊的迭代器均構造其偏特化版,通過“萃取”得出相應迭代器的特性
3.4 常用的迭代器型別
五種:value_type、difference_type、pointer、reference、iterator= catagoly
在模板類中定義如下:
template <class I>
struct iterator_traits {
typedef typename I::iterator_category iteerator_category;
typedef typename I::value_type value_type;
typedef typename I::difference_type difference_type;
typedef typename I::pointer pointer ;
typedef typename I::reference reference;
};
1. value type:指迭代器所指物件的型別
2. difference type:用來表示兩個迭代器之間的距離
3. reference type:引用型別
4. pointer type:指標型別
5. iterator_category:根據移動與施行操作,又可分為5類
- Input Iterator: 只讀迭代器
- Output Iterator:唯寫
- Forward Iterator:允許“寫入型“演算法在此迭代器上進行讀寫操作
- Bidirectional Iterator:可雙向移動
- Random Access Iterator:涵蓋上述所有指標運算能力,隨機的迭代器
3.5 dispatch到函式
- traits,一方面在面對不同的輸入類的時候能夠提取對應的型別;另一方面如果型別對應有不同的函式實現,則能起到提取型別並分流的作用
- 以下做了一個測試版本:
#include <iostream>
using namespace std;
//定義tag
struct A {};
struct B : public A {}; //1.以類來定義迭代器的各種分類標籤,不僅可以促成過載機制的成功運作
struct C : public B {}; //2.通過繼承,可以不必再寫“單純只做傳遞呼叫的函式
//定義一個類,裡面定義型別
template <class T>
struct Definition {
typedef T Judgement_type;
};
//特性萃取器
template <class Definition>
struct Definition_traits {
typedef typename Definition::Judgement_type Judgement_type;
};
//特性萃取器-針對原生指標版本
template <class T>
struct Definition_traits<T*> {
typedef T Judgement_type;
};
//特性萃取器-針對常量指標版本
template <class T>
struct Definition_traits<const T*> {
typedef const T Judgement_type;
};
//這個函式可以很方便地決定某個迭代器地型別
template <class Definition>
inline typename Definition_traits<Definition>::Judgement_type
Judgement_type(Definition){
typedef typename Definition_traits<Definition>::Judgement_type JT;
return JT();
}
//以下是整組func測試函式
template <class Definition>
inline typename Definition_traits<Definition>::Judgement_type
_func(Definition, A) {
cout << "A tag()" << endl;
return A();
}
//以下過載函式,根據型別dispatch
template <class Definition>
inline typename Definition_traits<Definition>::Judgement_type
_func(Definition, B) {
cout << "B tag()" << endl;
return B();
}
template <class Definition>
inline typename Definition_traits<Definition>::Judgement_type
_func(Definition, C) {
cout << "C tag()" << endl;
return C();
}
template <class Definition, class T>
inline T
_func(Definition, T) {
cout << "origin ptr" << endl;
return T();
}
template <class Definition>
inline typename Definition_traits<Definition>::Judgement_type
func(Definition u) {
typedef typename Definition_traits<Definition>::Judgement_type Judgement_type;
return _func(u, Judgement_type());
}
int main()
{
Definition<A> a;
Definition<B> b;
Definition<C> c;
int *p = NULL;
func(a);
func(b);
func(c);
func(p);
return 0;
}
- 理解了這一個實現大概就能理解traits程式設計的作用
4. traits的第二個應用–__type_traits
iterator_traits萃取迭代器的屬性,__type_traits萃取型別的屬性,根據不同的型別屬性,在編譯時期完成函式的派送決定
- 由於我們在上面已經詳述了關於traits程式設計,在這裡就不多分析;來分析一下__type_traits如何判斷是否有trivial(不重要的)函式:
如果至少滿足一下3條中的一條:
- 顯示(explict)地定義了這四種函式,ctor、copy、assignment、dtor
- 類裡有非靜態非POD地資料成員,如string
- 有基類
那麼上面四種函式是non-trivial ctor,non-trivial copy…(即有意義的函式),如果該類都是trivial函式,則可在對該型別構造、析構、拷貝、賦值操作時採用最有效率的措施
5. 總結
為什麼想寫這個總結呢?因為在上述理解traits程式設計時出現了一點小trouble,可能上面寫的有點亂,因此想寫個總結重新梳理一下
1.首先了解了迭代器在STL中的重要性以及迭代器這一設計模式
2.由於C++沒有獲得物件型別的直接方法,因此在嘗試了模板引數推導、內嵌型別方法之後,引出了traits程式設計
3.traits程式設計,利用內嵌型別以及通過模板的型別推導機制,獲得變數的型別,而後決定dispatch到哪個函式
4.traits程式設計技巧在STL中的應用:iterator_traits,__type_traits