3.2 迭代器是一种 smart pointer
迭代器是一种行为类似指针的对象,最重要的工作就是对operator *
和operator->
的重载。
3.4 Traits 编程技法
迭代器所指对象的类型是该迭代器的 value type。声明内嵌型别可以做到:
但是并不是所有迭代器都是类类型,这种声明不适用于指针。如果不是类类型就无法为它定义内嵌型别,但是 STL 又必须接受原生指针作为一种迭代器。
由此,可以使用偏特化,比如如下的一个模板类:
template<typename T>
class C{};
可以偏特化为:
template<typename T>
class C<T*>{}; //该特化版本仅适用于:T为原生指针的情况。
//T为原生指针 是 T为任何型别的更进一步的条件限制
现在就可以针对迭代器的模板参数为指针的类,设计特化版的迭代器。
下面的模板类专门用于萃取迭代器特性:
template<class I>
struct iterator_traits{
typedef typename I::value_type value_type;
};
//偏特化版本————迭代器是个原生指针
template<class T>
struct iterator_traits<T*>{
typedef typename T value_type;
};
//偏特化版本————当迭代器是个pointer-to-const时,萃取出来的应该是 T而不是 const T
template<class T>
struct iterator_traits<const T*>{
typedef typename T value_type;
};
意义是:如果I
有自己内部定义的value_type
,那么通过这个 traits 的过程,萃取出来的value_type
就是I::value_type
。于是之前的函数func()
就可以写成这样:
traits 可以方便地榨取迭代器的各种特性:
最常用到的迭代器相应特性有五种:value_type
、difference_type
、pointer
、reference
、iterator catagory
,一般要求容器的迭代器定义这五种相应型别:
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;
};
iterator_traits
必须对传入的类型为 pointer、pointer-to-const 者,设计特化版本。
3.4.1 value_type
value_type
指的是迭代器所指对象的型别,任何一个意图与 STL 算法适配的 class 都应该在内部定义自己的value_type
内嵌型别。
3.4.2 difference_type
用于表示两个迭代器之间的距离,因此也可以用于表示一个容器的最大容量。比如 STL 算法中的count()
的返回值就是difference_type
。
针对指针型别difference type
,traits 的两个特化版本以 C++ 内建的ptrdiff_t
作为原生指针的difference_type
:
template<class I>
struct iterator_traits{
...
typedef typename I::difference_type difference_type;
};
//针对原生指针的偏特化版本
template<class T>
struct iterator_traits<T*>{
...
typedef ptrdiff_t difference_type;
};
//针对原生的pointer-to-const偏特化版本
template<class T>
struct iterator_traits<const T*>{
...
typedef ptrdiff_t difference_type;
};
//自此,在需要任何迭代器I的difference_type时,可以这么写:
typename iterator_traits<I>::difference_type
3.4.3 reference_type
从迭代器所指内容给是否允许修改的角度,迭代器分为两种:不允许改变和允许改变。在C++中如果函数想传回左值,都是以 by reference 的方式进行,所以:
- 当 p 是一个可修改迭代器时,若其
value_type
是 T,那么*p的型别应该是T&
; - 当 p 是一个不可修改迭代器时,若其
value_type
是T,则*p的型别应该是const T&
3.4.4 pointer_type
可以传回一个指针,指向迭代器所指之物。
3.4.5 iterator_category
根据移动特性与施行操作,迭代器被分为五类:
- Input Iterator:这种迭代器所指对象不允许外界改变,只读。
- Output Iterator:只写。
- Forward Iterator:允许写入型算法在此种迭代器所形成的的区间上进行读写操作。
- Bidirectional Iterator:可双向移动。
- Random Access Iterator:前三种只支持
operator++
,第四种多支持operator--
,第五种则涵盖所有指针运算,包括+、-、[]、<、+=、-=.
有了这些迭代器的分类,在设计 STL 算法时,就会针对某种迭代器提供一个明确定义,并针对支持更多功能的迭代器提供另一种定义。
书中以advance()
和distance()
举例,P93 开始。
3.5 std::iterator 的保证
为了符合规范,任何迭代器都应该提供五个内嵌相应型别,以便于 traits 萃取,否则无法与 STL 的整个框架、算法适配。所以 STL 提供了一个 iterator class 如下,每个新设计的迭代器都应继承自它:
template<
class Category,
class T,
class Distance = std::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;
};
3.7 type_traits
TODO…