照順序,這次應該是迭代器Iterator的內容了,然而Iterator涉及到一個重要的技巧就是Traits編程技法。
一 獲取Iterator的相應類型(associate type)
在使用Iterator時,可能需要知道它的相應類型,也就是Iterator指向的變量的類型,在C/C++語言中,如果要獲取一個變量的大小可以使用sizeof()操作符。然而如果想要獲取一個指針指向的變量類型該如何做呢,可惜它沒有一個typeof()操作符供我們程序員使用。
利用template的引數/參數推導(argument deducation)是一個解決問題的好方法,僅將func函數作爲一個包裝,而把實際的操作放在一個函數func_impl裏面完成。一旦func()函數被調用,編譯器就自動進行引數推導,自動導出類型T。
#include <iostream>
using namespace std;
template<typename Point, typename T>
void func_imp(Point iter, T value)
{
T tmp; // 這裏推導出了iter的數據類型
tmp = value;
cout << tmp << " in " << __FUNCTION__ << endl;
}
template<typename Point>
void func(Point iter)
{
func_imp(iter, *iter);
}
int main()
{
int *a = new int;
*a = 10;
func(a);
return 0;
}
現在解決了從指針中獲取原數據類型的方法,類型的的確確是獲取到了,但要將類型作爲一個函數的返回類型呢?有沒有什麼辦法提前獲取到類型呢,這就需要Traits編程技法了。
二 Traits編程技法初見
採用nested type(巢狀型別)似乎是個不錯的注意,如下所示:
template<class T>
class Iterator {
public:
typedef T value_type;
T *m_ptr;
Iterator(T *p = 0) : m_ptr(p) {}
T& operator*()const { return *m_ptr;}
};
template<class I>
typename I::value_type func2(I iter)
{
return *iter;
}
int main()
{
int *p = new int(8);
Iterator<int> iter(p);
cout << func2(iter) << endl;
delete p;
return 0;
}
首先構建Iterator類,獲取類型的方法和上文直接用兩層函數的方法相似。這裏func2函數的返回值前加上了一個typename,這是因爲在template T實例化之前,編譯器對T一無所知,並不知道Iterator<int>::value_type代表的是一個函數,變量還是類型。關鍵字typename就是告訴編譯器說這是一個類型,以使得編譯通過。
看起來不錯,但是這裏還有一個隱晦的陷阱:並不是所有的迭代器都有value_type,編譯器內嵌類型(原生指標)就沒有,這樣編譯就不能通過,但是STL必須接受原生指標作爲一種迭代器,這需要另外的技巧,它就是模板偏特化(template partial specialization)。