1.迭代器作用
迭代器(iterators)是一种抽象的设计概念,现实程序语言中并没有直接对应于这个概念的实物。《Design Patterns》中 iterator模式定义如下:提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表述方式。STL的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶合剂(iterator)将它们撮合在一起。 迭代器是数据结构容器中非常有用的“特殊指针”,在不暴露容器中结构的条件下,可以取出容器里所有的元素。
2.迭代器是一种智能指针
迭代器是一种行为类似指针的对象,而指针的各种行为中最常见也最重要的便是内容提领( dereference)和成员访问( member access),因此,迭代器最重要的编程工作就是对operator* 和operator->进行重载overloading)工作。
3.不同的容器都有专属的迭代器
为了实现细节得以封装起来不被使用者看到,每一种STL容器都需要提供专属迭代器。那么问题就来了: 在算法中运用迭代器时,很可能会用到相应类型(associated type)。什么是相应类型?迭代器所指之物的类型便是其中之一。假设算法中有必要声明一个变量,以“迭代器所指对象的类型”为类型,如何处理?
3.1函数模板类型自推
为了判断迭代器的类别,需要进行参数推导。解决办法是:利用function template的参数推导( argument deducation)机制。所谓 “自推” 指的是编译器在某些情况下,可以根据调用方提供的信息来补全用户未提供的模板参数,是模板实例化 (template instantiation) 的一个步骤,发生的时机是在函数模版调用时 (invoke time of function template)。也就是说,当需要的时候,每次模版函数的调用,均会 (根据调用方提供的信息) 触发一次潜在的模板参数类型推导。
#include<iostream>
using namespace std;
/*一个功能不完备的迭代器,但用于举例已经足够了,只需要实现deference运算符重载*/
template<class Item>
class iter
{
public:
typedef Item* pointer;
typedef Item& reference;
public:
iter(pointer ptr = 0) //默认构造方法
:_ptr(ptr){ }
reference operator*() const { return *_ptr; } //dereference
private:
pointer _ptr;
};
template<class I, class T>
void func_impl(I iter, T type)
{
T output = *iter;
cout << "利用函数模板类型自推:";
cout << output << endl;
}
template<class I>
inline
void func(I iter) //func函数实际上是对外接口
{
func_impl(iter, *iter);
}
int main()
{
int i = 10;
func(&i);
return 0;
}
我们以 func ()为对外接口,却把实际操作全部置于func_.impl (〉之中。由于func_impl ( 〉是一个function template,一旦被调用,编译器会自动进行template参数推导。于是导出型别T,顺利解决了问题。
“为了与C保持兼容,返回值并非是调用函数时的必要条件,因此函数模版类型推导和函数重载都不能且不应依赖返回值。”
函数模板类型自推是有局限性的,对于上述代码,试图通过返回值类型推导出T到底是什么类型是可取的。但,如果函数返回值类型为void,参数类型列表(parameter type list)为空,那么无论怎样都无法处理,需要另辟蹊径。引出了声明内嵌类型这一手段。
3.2声明内嵌类型
#include<iostream>
using namespace std;
template<class T>
class iter
{
public:
typedef T* pointer;
typedef T& reference;
typedef T value_type; //声明内嵌类型
public:
iter(pointer p = 0)
:_ptr(p){ }
reference operator *()const
{
return *_ptr;
}
private:
pointer _ptr;
};
template<class I>
typename I::value_type //返回值类型
func(I ite)
{
return *ite;
}
int main()
{
int a = 10;
iter<int> ite(&a);
cout << func(ite) << endl;
return 0;
}
注意,func()的回返型别必须加上关键词typename,因为T是一个template参数,在它被编译器具现化之前,编译器对T 一无所悉,换句话说,编译器此时并不知道MyIter<T> : : value_type代表的是一个型别或是一个memberfunction或是一个data member。关键词typename 的用意在于告诉编译器这是一个型别,如此才能顺利通过编译。
但并不是所有迭代器都是class type。原生指针(类型* 变量名,可以理解为一般的指针)就不是,如果不是 class type,就无法为它定义内嵌型别。但 STL(以及整个泛型思维)绝对必须接受原生指针作为一种迭代器,所以上面这样还不够。有没有办法可以让上述的一般化概念针对特定情况(例如针对原生指针)做特殊化处理呢?
3.3类型萃取机
Partial Specializa tion(偏特化〉的意义: template partial specialization,大致的意义是:如果 class template拥有一个以上的template参数,我们可以针对其中某个(或数个,但非全部) template 参数进行特化工作。换句话说,我们可以在泛化设计中提供一个特化版本(也就是将泛化版本中的某些template 参数赋予明确的指定)。
partial specialization的字面意义容易误导我们以为,所谓“偏特化版”一定是对template参数u或v或T(或某种组合)指定某个参数值.其实不然,[Austern99]对于partial specialization的意义说得十分得体:“所谓partial specialization的意思是提供另一份template定义式,而其本身仍为templatized”。《泛型思维》一书对 partial specialization的定义是:“针对(任何) template参数更进一步的条件限制所设计出来的一个特化版本”。由此,面对以下这么一-个class template:
//我们更容易接受它有一个形如下的Partial Specializationtemplate<typename T>class C<T*> {……}; //这个泛化版本仅适用于”T为原生指针”的情况,便是”T为任何型别”的一个更进一步的条件限制。
假设有一个class template 如下:
template<typename U, typename V, typename T>
class C {……}; //这个泛化版本允许(接受)T为任何型别
//我们更容易接受它有一个形如下的Partial Specialization
template<typename T>
class C<T*> {……}; //这个泛化版本仅适用于"T为原生指针"的情况,便是"T为任何型别"的一个更进一步的条件限制。
于是,我们可以解决前述“内嵌型别”未能解决的问题。先前的问题是,原生指针并非class,因此无法为它们定义内嵌型别。现在,我们可以针对“迭代器之template参数为指针”者,设计特化版的迭代器。
template<class T>
struct miterator_traits<T*> //1号
{
typedef T value_type;
};
int main()
{
miterator_traits<const int*>::value_type test1;
test1 = 2; //会报错!!!常量不可修改
return 0;
}
template<class T>
struct miterator_traits<T*> //1号
{
typedef T value_type;
};
int main()
{
miterator_traits<const int*>::value_type test1;
test1 = 2; //会报错!!!常量不可修改
return 0;
}
在代码中出现的1号萃取机参数类型是const int*,经过萃取之后,T经过编译器确认是const int。我们需要提取出类型int(non-const),应该加上一个新版本。
template<class T> //2号
struct miterator_traits<const T*>
{
typedef T value_type;
};
int main()
{
miterator_traits<const int*>::value_type test1;
test1 = 2;
cout << test1 << endl;
system("pause");
return 0;
}
此时得到正确结果:
4、迭代器分类
在STL中,原生指针也是一种迭代器,除了原生指针以外,迭代器被分为五类:
1、Input Iterator:
此迭代器不允许修改所指的对象,即是只读的。支持==、!=、++、、->等操作。
2、Output Iterator
允许算法在这种迭代器所形成的区间上进行只写操作。支持++、等操作。
3、Forward Iterator:
允许算法在这种迭代器所形成的区间上进行读写操作,但只能单向移动,每次只能移动一步。支持Input Iterator和Output Iterator的所有操作。
4、Bidirectional Iterator:
允许算法在这种迭代器所形成的区间上进行读写操作,可双向移动,每次只能移动一步。支持Forward Iterator的所有操作,并另外支持–操作。
5、Random Access Iterator:
包含指针的所有操作,可进行随机访问,随意移动指定的步数。支持前面四种Iterator的所有操作,并另外支持it + n、it – n、it += n、 it -= n、it1 – it2和it[n]等操作。
这些迭代器的分类与从属关系,可以用图3-2表示。直线与箭头代表的并非C++的继承关系,而是所谓concept(概念)与refinement(强化)的关系。
5.总结:
1、类型萃取机为什么会存在?
迭代器主要分两类:一个是类去实现(常见如iterator),另一个是以原生指针实现。
函数模板类型自推:解决了推导参数类型列表的问题,但没有完全解决其他问题,如定义返回值类型等。这个时候就出现了内嵌类型声明;
内嵌类型声明:解决了迭代器是以类实现的情况,无法解决迭代器以原生指针实现的情况。就出现了萃取机;
类型萃取机: 出现的目的主要是为了解决原生指针类型萃取所存在的问题(因为手段2无法解决),原生指针的问题解决了之后,把类去实现的迭代器也作为类型萃取机的操作对象的话,类型萃取机就能处理所有的迭代器了(类型萃取机就该做到这一点)。而需要处理类实现的迭代器相应类型的提取,只不过多了一层间接性而已。正如代码中0号萃取机所展示的那样。
2.示意图
3.总结:
设计适当的相应型别 ( associated types),是迭代器的责任。设计适当的迭代器,则是容器的责任。唯容器本身,才知道该设计出怎样的迭代器来遍历自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员…)。至于算法,完全可以独立于容器和迭代器之外自行发展,只要设计时以迭代器为对外接口就行。
traits 编程技法大量运用于STL实现品中。它利用“内嵌型别”的编程技巧与编译器的template参数推导功能,增强C++未能提供的关于型别认证方面的能力,弥补C++不为强型别( strong typed)语言的遗憾。
6.iterator源码
#ifndef _ITERATOR_H_
#define _ITERATOR_H_
#include<cstddef>
namespace EasySTL
{
//五种迭代器,作为标记型别(tag types),不需要任何成员
struct input_iterator_tag{};
struct output_iteratoe_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{};
template<class T,class Distance>
struct input_iterator
{
typedef input_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
template<class T,class Distance>
struct output_iterator
{
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
};
template<class T,class Distance>
struct forward_iterator
{
typedef forward_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
template<class T,class Distance>
struct bidirectional_iterator
{
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
template<class T,class Distance>
struct random_access_iterator
{
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
//为避免写代码时挂一漏万,自行开发的迭代器最好继承自下面这个 std::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;
}
//"榨汁机"traits
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;
}
//针对原生指针(naive pointer)而设计的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;
};
//针对Pointer-to-const而设计的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;
};
//判断某iterator的类型
template<class Iterator>
inline typename iterator_traits<Iterator>::iterator_category
iterator_category(const Iterator& It)
{
typedef typename iterator_traits<Iterator>::iterator_category
return category();
}
//获取迭代器的value_type
template<class Iterator>
inline typename iterator_traits<Iterator>::value_type*
value_type(const Iterator& It)
{
return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
}
//获取迭代器的distance_type
template<class Iterator>
inline typename iterator_traits<Iterator>::difference_type*
distance_type(const Iterator& It)
{
return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
}
//....................................................................
/*函数重载,使迭代器能在编译时期就确定调用哪个函数*/
template <class _InputIter, class _Distance>
/*迭代器类型为input_iterator_tag的函数定义*/
inline void __advance(_InputIter& __i, _Distance __n, input_iterator_tag) {
while (__n--) ++__i;
}
template <class _BidirectionalIterator, class _Distance>
/*迭代器类型为bidirectional_iterator_tag的函数定义*/
inline void __advance(_BidirectionalIterator& __i, _Distance __n,
bidirectional_iterator_tag) {
__STL_REQUIRES(_BidirectionalIterator, _BidirectionalIterator);
if (__n >= 0)
while (__n--) ++__i;
else
while (__n++) --__i;
}
template <class _RandomAccessIterator, class _Distance>
/*迭代器类型为random_access_iterator_tag的函数定义*/
inline void __advance(_RandomAccessIterator& __i, _Distance __n,
random_access_iterator_tag) {
__STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator);
__i += __n;
}
template <class _InputIterator, class _Distance>
/*决定调用哪个函数,这是一个对外接口*/
inline void advance(_InputIterator& __i, _Distance __n) {
__STL_REQUIRES(_InputIterator, _InputIterator);
__advance(__i, __n, iterator_category(__i));
}
//....................................................................
template<class InputIterator>
inline typename std::iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last){
return __distance(first, last, std::iterator_traits<InputIterator>::iterator_category());
}
template<class InputIterator>
inline typename std::iterator_traits<InputIterator>::difference_type= 0;
while (first != last){
++first; ++n;
}
return n;
}
template<class InputIterator>
inline typename std::iterator_traits<InputIterator>::difference_type \
__distance(InputIterator first, InputIterator last, std::random_access_iterator_tag){
return last - first;
}
//....................................................................
}
#endif
7.参考文献:
侯捷《STL源码剖析》