1. 程式人生 > >STL原始碼分析之traits萃取劑

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結構體並沒有對原生指標做處理, 所以還要為特化, 偏特化版本(即原生指標)做統一.

以下便是iterator_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之全特化和偏特化