1. 程式人生 > >迭代器(iterators)與traits程式設計技巧

迭代器(iterators)與traits程式設計技巧

//iterator_traits的預設版本
template<typename I>
class iterator_traits
{
	//每種容器的迭代器都必須定義value_type,value_type,pointer,reference,iterator_category
	typedef typename I::value_type value_type;
	typedef typename I::difference_type value_type;
	typedef typename I::pointer pointer;
	typedef typename I::reference reference;
	typedef typename I::iterator_category iterator_category;
}
//針對 I為 原始指標的特化版本
template<typename T>
class iterator_traits<T *>
{
	typedef  T value_type;
	typedef ptrdiff_t difference_type;//build-in 型別ptrdiff_t定義在<cstddef>標頭檔案中
	typedef T* pointer;
	typedef T & reference;
	typedef random_access_iterator_tag iterator_category;//關於iterator_category,下面會詳細介紹
}
//針對 I為 const原始指標的特化版本
template<typename T>
class iterator_traits<const T *>
{
	typedef  T value_type;
	typedef ptrdiff_t difference_type;//build-in 型別ptrdiff_t定義在<cstddef>標頭檔案中
	typedef const T* pointer;
	typedef const T & reference;
	typedef random_access_iterator_tag iterator_category;//關於iterator_category,下面會詳細介紹
}

迭代器的型別 iterator_category

迭代器被分為五類:

Input iterator :只讀(read only)

Output iterator:只寫(write only)

Forward Iterator:讀寫(read and write)

Bidirectional Iterator:雙向迭代器,可前進可後退。

Random Access Iterator:可隨機訪問,跳躍式,p+n,p-n,p[n],p1-p2,p1<p2等都支援。

舉個例子說明iterator_traits怎麼處理五種型別迭代器的,同時對traits程式設計技巧加上印象。

實現advance(I,n)函式,兩個引數I表示迭代器,n表示前進的距離。針對不同型別的迭代器,呼叫效率最高的函式版本。

思路一:

定義多個版本,在對外介面中判斷呼叫哪個版本。

//針對input iterator
template<typename InputIterator,typename Distance>
void _addvance_II(InputIterator & i,Distance n)
{
	while(n--)++i;
}
//針對 bidirectional  iterator
template<typename Bidirectional,typename Distance>
void _addvance_IB(Bidirectional & b,Distance n)
{
	if(n>=0)
        while(n--)++b;
	else	
		while(n++)--b;
}
//針對Random  Access Iterator
template<typename RandomAccessIterator,typename Distance>
void _addvance_RAI(RandomAccessIterator & r,Distance n)
{
	r+=n;
}

//對外介面
template<typename InputIterator,typename Distance>
void advance(InputIterator i,Distance n)
{
	//在這裡判斷i到底是哪種型別的迭代器
	if(is_input_iterator(i))
		_addvance_II(i,n);
	else if(is_bidirectional_iterator(i))
		_addvance_IB(i,n);
	else
		_addvance_RAI(i,n);
}

這種方法有個嚴重的缺陷:在執行期間才確定執行哪個版本,影響效率,最後在編譯期間就能決定呼叫哪個函式。函式過載可解決這個問題,1.為了實現過載機制需要提供額外的引數來區分不同函式;2.然後再在對外介面內確定該額外引數的型別。
//五個作為標記的型別
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag:public input_iterator_tag{};
struct bidirectional_iterator:public forward_iterator_tag{};
struct random_access_iterator_tag:public bidirectional_iterator{};

//針對input iterator
template<typename InputIterator,typename Distance>
void _addvance(InputIterator & i,Distance n,input_iterator_tag)
{
	while(n--)++i;
}
//forward iterator版本不用定義,因為它的實現與 input iterator版本完全一樣
//上面的五個型別標記的繼承關係使得 當傳進來的迭代器型別是forward iterator時
//由於沒有定義該版本的_advance,所以會呼叫基類input iterator版本的_advance.

//針對 bidirectional  iterator
template<typename Bidirectional,typename Distance>
void _addvance(Bidirectional & b,Distance n,bidirectional_iterator)
{
	if(n>=0)
        while(n--)++b;
	else	
		while(n++)--b;
}
//針對Random  Access Iterator
template<typename RandomAccessIterator,typename Distance>
void _addvance(RandomAccessIterator & r,Distance n,random_access_iterator_tag)
{
	r+=n;
}

//對外介面
template<typename InputIterator,typename Distance>
void advance(InputIterator i,Distance n)
{
	//這裡利用iterator_traits判斷呼叫哪個版本的_advance
	_addvance(i,n,iterator_traits<InputIterator>::iterator_category());
}


為了規範,任何迭代器都需提供上述五個內嵌型別,以利用traits提取特性。為了方便,STL提供了一個基類 iterator,每個特定的迭代器只需繼承該基類即可。

template<typename Category,

         typename T,
		 typename Distance=pdfdiff_t,
		 typename Pointer= T*,
		 typename Reference=T&;>
class iterator
{
	typedef Category iterator_category;
	typedef T value_type;
	typedef Distance difference_type;
	typedef Pointer pointer;
	typedef Reference reference;
}

//例如 list的迭代器,型別為forward iterator
template<typename Item>
class struct ListIter:public std::iterator<std::forward_iterator_tag,Item>
{
	//......
}