总结一下,我们之所以要萃取迭代器相关的类型,无非是要把迭代器相关的类型用于声明局部变量、用作函数的返回值等一系列行为。对于原生指针和point-to-const类型的指针,采用模板偏特化技术对其进行特殊处理,另外,对于point-to-const类型的指针,为了保证声明左值时语义正确,特化时按照普通原生指针处理。<?xml:namespace prefix = o />
实际上,把我们刚才的例子提炼一下,迭代器相应类型不仅仅有迭代器封装的对象类型,STL中对这些类型作了整理,有如下几种:
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分别对其进行了特化处理。如果你看了上面的代码却不知所云,也属正常,在去了解特化版本之前,我们先来看看这五种类型的含义。
Type 1 value_type
这个类型和我们在第一部分谈到的vlaue_type的含义是一样的,不多说了。
Type 2 difference_type
用来表示两个迭代器之间的最大距离。这个类型用来对某种算法提供计数功能,例如:
template <class I, class T>
typename iterator_traits<I>::difference_type
count(I first, I last, const T& value){
typename iterator_traits<I>::difference_type n = 0;
for(; first != last; first++) {
if(*first == value)
n++;
}
return n;
}
也许这个例子最足以说明问题,任何的解释都没必要了。这里需要说明的是。对于原生指针,由于不存在int::difference_type的情况,所以,iterator_traits对其进行特化:
template <class I>
class iterator_traits<I*> {
typedef ptrdiff_t difference_type;
}
这里,ptrdiff_t是定义在cstddef中的一个C++内置类型,在GNU gcc中,定义如下:
typedef long int ptrdiff_t;
同样,对于pointer-to-const,也要入法炮制:
template <class I>
class iterator_traits<const I*> {
typedef ptrdiff_t difference_type;
}
再一次,偏特化技术帮了大忙,现在count可以处理所有类型迭代器的difference_type了。
Type 3 reference
这里,reference type指的是迭代器封装对象的类型的引用。这个类型的出现主要是为了解决对指针进行解引用的时候,返回什么样的对象的问题。我们希望:
MyIter<int> iter(new int(10));
*iter = 52;
和
Int *p = new int(10);
*p = 52;
是一样的。于是,reference_type一般用在迭代器的*运算符重载上,让所有的“指针家族”有同样的表现形式。于是,如果value_type是T,那么reference_type就是T&,如果value_type是const T,reference_type就是const T&。
Type 4 pointer
C++中指针和引用总是有着密切的关系。如果我们想返回迭代器封装的对象的地址,就需要用到这里的pointer_type,主要用在迭代器中对->运算符重载的问题。对于一个智能指针来说,通常我们都需要下面的两个运算符重载:
T& operator*() const { return *ptr; } // T& is reference type
T* operator->() const { return ptr; } // T* is pointer type
同样,为了能够对迭代器和原生指针都能够在算法上有统一的表现形式,在iterator_traits中加入了下面的类型
template <class T>
struct iterator_traits {
typedef typename I::pointer pointer;
typedef typename I::reference reference;
}
同样,对于原生指针和point-to-const类型的指针作了特化:
template<class T>
struct iterator_traits<T*> {
typedef typename T* pointer;
typedef typename T& reference;
}
而这次,对于point-to-const类型的指针,则有些特别:
template<class T>
struct iterator_traits<const T*> {
typedef typename const T* pointer;
typedef typename const T& reference;
}
也就是说,当我们解引用一个封装了常量对象的迭代器的时候,返回的类型应该是const T&,取一个封装了常量对对象的迭代器中的元素的地址,返回的应该是const T*。最终的结果,就是所有的算法都有了一个统一的表达方式:
template <class T>
typename iterator_traits<T>::reference func() {}
template <class T>
typename iterator_traits<T>::pointer func() {}
Type 5 iterator_category
这个类型的作用是按照迭代器的移动特性和能够在该迭代器上实施的操作对迭代器进行分类,之所以这样做,完全是为了效率的考量。不过,在我看来,对其分类的因素实际上只有迭代器的移动特性,而分类,也非常简单:一步步向前挪的类型和一步跨到位的类型。