STL原始碼分析之traits萃取劑
前言
前面我們分析了迭代器的五類, 而迭代器所指向物件的型別被稱為value type
. 傳入引數的型別可以通過編譯器自行推斷出來, 但是如果是函式的返回值的話, 就無法通過value type
讓編譯器自行推斷出來了. 而traits
就解決了函式返回值型別. 同樣原生指標不能內嵌型別宣告,所以內嵌型別在這裡不適用, 迭代器無法表示原生指標(int *, char *等稱為原生指標). 這個問題就通過traits
偏特化技術解決的. 這一篇我們就主要探討traits
是怎麼實現這些沒有能解決的問題.
iterator_traits結構
iterator_traits
結構體就是使用typename
template <class Iterator>
struct iterator_traits {
typedef typename Iterator::iterator_category iterator_category; //迭代器型別
typedef typename Iterator::value_type value_type; // 迭代器所指物件的型別
typedef typename Iterator::difference_type difference_type; // 兩個迭代器之間的距離
typedef typename Iterator::pointer pointer; // 迭代器所指物件的型別指標
typedef typename Iterator::reference reference; // 迭代器所指物件的型別引用
};
在五類迭代器對模板物件的型別重新定義一次. 這裡提取(萃取)出來的引數型別名都是統一的, 也就說明每個要使用traits
程式設計的類必須以此型別名為標準, 而且需要自己對類定義這些型別名.
上面的traits
結構體並沒有對原生指標做處理, 所以還要為特化, 偏特化版本(即原生指標)做統一.
// 針對原生指標 T* 生成的 traits 偏特化
template <class T>
struct iterator_traits<T*> {
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};
// 針對原生指標 const T* 生成的 traits 偏特化
template <class T>
struct iterator_traits<const T*> {
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef const T* pointer;
typedef const T& reference;
};
這樣不管是函式返回值型別還是原生指標都能通過萃取器萃取出來, typename I::型別
進行型別萃取.
前面也分析了一下iterator_category
函式, 現在再來看一下就能明白, 該函式是通過iterator_traits
萃取的型別的iterrator_category
確定該迭代器的型別的, 五類迭代器都設定了不同的iterator_category
的值, 最後呼叫category()
函式確定傳入引數的型別.
template <class Iterator>
inline typename iterator_traits<Iterator>::iterator_category
iterator_category(const Iterator&) {
typedef typename iterator_traits<Iterator>::iterator_category category;
return category();
}
// category的五類型別
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
繼續看distance
函式, __distance
接受的前三個引數都是一樣, 唯一不一樣的就是最後一個引數, 通過iterator_category
函式萃取出迭代器的型別從而根據型別而執行其對應的__distance
函式.
// 根據第三個引數的型別呼叫相應的過載函式
template <class InputIterator, class Distance>
inline void distance(InputIterator first, InputIterator last, Distance& n)
{
__distance(first, last, n, iterator_category(first));
}
template <class InputIterator, class Distance>
inline void __distance(InputIterator first, InputIterator last, Distance& n,
input_iterator_tag)
{plate <class InputIterator, class Distance>
inline void __distance(InputIterator first, InputIterator last, Distance& n,
input_iterator_tag)
{
while (first !=
while (first != last)
{ ++first; ++n; }
}
template <class RandomAccessIterator, class Distance>
inline void __distance(RandomAccessIterator first, RandomAccessIterator last,
Distance& n, random_access_iterator_tag)
{
n += last - first;
}
這裡又列出了兩個型別的實現, 這裡用到了0可以轉換成指標的性質, 相當於返回一個空指標, 但是可以通過它們確定不同的引數型別.
template <class Iterator>
inline typename iterator_traits<Iterator>::difference_type*
distance_type(const Iterator&) {
return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
}
template <class Iterator>
inline typename iterator_traits<Iterator>::value_type*
value_type(const Iterator&) {
return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
}
value_type
在空間配置器的時有提過, 就是關於destory
的第二個版本.
// 第二個版本的, 接受兩個迭代器, 並設法找出元素的型別. 通過__type_trais<> 找出最佳措施
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last)
{
__destroy(first, last, value_type(first));
}
總結
traits
程式設計使用typename
和特化, 偏特化將迭代器沒能支援原生指標, 不能推匯出函式返回值的問題完善了. 同時traits
程式設計技法對迭代器加以規範, 提前知道了物件的型別相關資訊, 從而選擇最優的函式執行, 少了型別轉化, 提高了執行效率.
如果對template的特化沒有明白的, 這裡整理了一篇關於偏特化與全特化的介紹, template之全特化和偏特化