《STL源碼剖析》之Traits

不論是泛型思維或STL的實際運用,迭代器都扮演着重要的角色。STL的中心思想在於:將數據容器和算法分開,彼此獨立設計,最後再以一帖強力膠將他們粘在一起。容器和算法的泛型化,從技術角度來看,並不困難,C++的 class templates和function templates可分別達成目標。而迭代器正是這一帖強力膠。

但是,在實現泛型算法的時候,雖然有template,但是對於同一功能,不同迭代器類型,可能具有不同的操作。比如vector的迭代器進行遞增與list的迭代器進行遞增的內部實現是不一樣的,所以我們需要知道迭代器的相應型別,即迭代器所屬的類型。

1、迭代器相應型別

假設算法中有必要聲明一個變量,以“迭代器所指對象的型別”爲型別,這該如何解決?C++只支持sizeof(),並不支持typeof()。解決辦法是:利用function template的參數推導機制。例如:

template <class I, class T>
void func_impl(I iter, T t)
{
    T temp;
    cin>>temp;
    cout<<temp<<endl;
}

template <class I>
inline void func(I iter)
{
    func_impl(iter, *iter);
}

int main()
{
    int i;
    func(&i);
    return 0;
}
我們以func()爲對外接口,卻把實際操作都放在func_impl()中,順利解決問題。

2、Traits編程技法——STL源代碼門鑰

迭代器所指對象的型別,稱爲該迭代器的value type。上述的參數型別推導技巧並非全面可用:萬一value type用於函數的返回值,就束手無策,畢竟函數的“template 參數推導機制”針對的只是參數。我們可以利用“聲明內嵌型別”:

template <class T>
struct MyIter
{
    typedef T value_type;
    T* ptr;
    MyIter(T* p=0):ptr(p){}
    T& operator*() const {return *ptr;}
};
template<class I>
typename I::value_type
func(I ite)
{ return *ite; }
注意,func()的返回類型必須加上typename,因爲I是一個template參數,在它被編譯器具體化之前,編譯器對T一無所知,關鍵字typename的用意在於告訴編譯器這是一個型別,才能順利編譯通過。

看起來不錯,兩個問題都解決了,但是不是所有的迭代器都是class type。比如原生指針就不是。如果不是class type,就無法爲它定義內嵌型別,這個問題可以用template中的Partial Specialization(模板特例化)來解決。

(1)函數模板特例化:關鍵字template後跟一個“<>”

template<typename T>
int compare(const  T&, const T&);
template<>
int compare(const char* const &p1, const char* const &p2);
(2)類模板特例化:

template<>
struct foo<int>{};
Partial specialization的定義是:針對template參數更進一步的條件限制所設計出來的一個特化版本。比如,面對以下這麼一個class template:

template<typename T>
class c{};
我們很容易接受它有一個形式如下的partial specialization:

template<typename T>
class c<T*>{};
有了這個,我們便可以解決前面提到的“內嵌類型”不能解決的問題。下面進入關鍵地帶,下面這個class template專門用來“萃取”迭代器的特性,而value type正是迭代器的特性之一:

template <class I>
struct iterator_traits
{
    typedef typename I::value_type value_type;
}
那麼先前定義的那個func()可以改寫成:

template<typename T>
typename iterator_traits<T>::value_type
func(I ite)
{ return *ite; }
但這除了多一層間接性,又帶來什麼好處呢?好處是traits可以擁有特化版本,現在,我們令iterator_traites擁有一個partial specializations如下:

template <class T>
struct iterator_traits<T*>
{
    typedef T value_type;
}
於是,原生指針 int* 雖然不是一種class type,亦可通過traits取其value type。解決了先前的問題。

但是,針對“指向常數對象的指針”,下面這個式子得到什麼結果:

iterator_traits<const int*>::value_type
獲得的是const int而非int。針對這種情況,我們需要另外特例化一個版本。

最常用的迭代器相應型別有五種:value type、difference type、pointer、reference、iterator catagoly。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章