【STL源码剖析】__iterator_traits技法

__iterator_traits技法用于模板编程,STL所有算法都是基于模板实现的。

先看下面是一个例子:

template <class I, class T>
void func_impl(I iter, T t)
{
	// ...
}

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

int main()
{
	int i;
	func(&i);
	return 0;
}

这里利用了function template的参数推导机制。

但是,想象一下,“template参数推导技术”只是推出参数,无法推导返回值,怎么办?我们可以在迭代器里内嵌一个型别声明。如下:

template<class T>
struct MyIter
{
	typedef T value_type;
	// ....
};

template<class I>
typename I::value_type func(T iter)
{
	return *iter;
}

通过在迭代器类里面嵌入一个声明,解决了推导返回值的问题(注意:上面要用typename,因为MyIter是个模板,编译器对T模板参数一无所知)!

但问题又来了,并不是所有类型都是calss type,所以无法为它定义内嵌型别,template partial specialization(模板偏特化)可以做到,但要用另一个模板作为中间层进而偏特化(这里就是__iterator_traits技法了),如下:

//__iterator_traits
template<class I>
struct iterator_traits
{
	typedef typename I::value_type value_type;
};
//__iterator_traits偏特化版本,准确接受内置指针类型
template<class I>
struct iterator_traits<T*>
{
	typedef T value_type;
};
//接受指向const类型数据的指针
template<class I>
struct iterator_traits<const T*>
{
	typedef T value_type;
};

上面“无法推导返回值类型”的问题可以这样解决了:

template<class T>
struct MyIter
{
	typedef T value_type;
	// ....
};

template<class I>
typename iterator_traits<I>::value_type func(I iter)
{
	return *iter;
}

这就是__iterator_traits编程技法。

不过,在《stl源码剖析》是在2002出版的,我看了下只有第一版,那时候还没有C++11,在C++11的新语法下,我们其实可以这样解决“无法推导返回值”的问题,那就是利用auto、decltype、尾置返回语法的性质

template<class T>
struct MyIter
{
	typedef T value_type;
	// ....
};
template<class I>
auto func(I iter) -> typename remove_reference<decltype(*iter)>::type
{
	return *iter;
}
因为在推导返回值时,参数类型已有函数实参推断出来了,再用尾置返回,搞定。同时,要用decltype(*iter)得到的是引用类型,故需要remove_reference。


所以,STL里的算法模板都是这样实现,如果我们要设计与STL水乳交融的类,那就要符合STL的一些基本要求了。

1.设计类

2.设计类的迭代器

迭代器要满足STL的要求,那么你的迭代器就能用在STL算法。


STL有5中基本型别,它的特性萃取机这样定义的:

template<class I>
struct iterator_traits
{
	typedef typename I::iterator_category iterator_category;
	typedef typename I::value_type value_type;
	typedef typename I::difference_type difference_type;
	typedef typename I::pointer pointer;
	typedef typename I:;reference reference;
}
当然了, 还有针对pointer和pointer-to-const者设计特例化版本,这些在STL内部已经实现了我们需要做的是,设计一个迭代器,里面要嵌入5中型别的定义

iterator_category有什么用?它是用来判别这个迭代器(或许是指针)是什么类型的,根据移动特性与施行操作,迭代器被分为5类:

①Iuput iterator,只读

②Output iterator,只写

③Foreard iterator,可读写

④Bidirection iterator,可双向移动

⑤Random Access iterator,前四种提供基本操作(operator++,第四种在加上operator--),这种包含所有指针算术能力(p+n,p-n,p[n],p1<p2...)

为了分别五种型别,STL内部定义了5个类来区分:

struct input_iterator_tag{};
struct output_iteraotr_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{};

所以要设计能使用STL算法的迭代器,就要内嵌5种型别的定义,我们可以继承stl定义的iterator:

template<class Category, class T, class Distance=ptrdiff_t, class Pointer=T*, class Reference=T&>
struct iterator
{
	typedef Category iterator_category;
	typedef T value_type;
	typedef Distance difference_type;
	typedef Pointer pointer;
	typedef Reference reference;
};

那么STL算法是如何运用这些机制的呢,一个例子如下:

template<class InputIterator, class Distance>
inline void __advance(InputIterator &i, Distance n, input_iterator_tag)
{
	while (n--)++i;
}
template<class ForwardIterator, class Distance>
inline void __advance(ForwardIterator &i, Distance n, forward_iterator_tag)
{
	__advance(i, n, input_iterator_tag());
}

template<class BidirectionIterator, class Distance>
inline void __advance(BidirectionIterator &i, Distance n, bidirectional_iterator_tag)
{
	if (n >= 0)
		while(n--) ++i;
	else 
		while(n++) --i;
}

template<class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator &i, Distance n, random_access_iterator_tag)
{
	i += n;
}

template<class Iterator, class Distance>
inline void advance(Iterator &i, Distance n)
{
	__advance(i, n, iterator_traits<Iterator>::iterator_category());
}

上面,advance对我们可见,通过iterator_category来区分了不同的迭代器,以上就是__iterator_traits的全部了。





你可能感兴趣的:(源码,算法,iterator,STL)