3.2 迭代器是一种 smart pointer

迭代器是一种行为类似指针的对象,最重要的工作就是对operator *operator->的重载。

3.4 Traits 编程技法

迭代器所指对象的类型是该迭代器的 value type。声明内嵌型别可以做到:image.png
但是并不是所有迭代器都是类类型这种声明不适用于指针。如果不是类类型就无法为它定义内嵌型别,但是 STL 又必须接受原生指针作为一种迭代器
由此,可以使用偏特化,比如如下的一个模板类:

  1. template<typename T>
  2. class C{};

可以偏特化为:

  1. template<typename T>
  2. class C<T*>{}; //该特化版本仅适用于:T为原生指针的情况。
  3. //T为原生指针 是 T为任何型别的更进一步的条件限制

现在就可以针对迭代器的模板参数为指针的类,设计特化版的迭代器。

下面的模板类专门用于萃取迭代器特性

  1. template<class I>
  2. struct iterator_traits{
  3. typedef typename I::value_type value_type;
  4. };
  5. //偏特化版本————迭代器是个原生指针
  6. template<class T>
  7. struct iterator_traits<T*>{
  8. typedef typename T value_type;
  9. };
  10. //偏特化版本————当迭代器是个pointer-to-const时,萃取出来的应该是 T而不是 const T
  11. template<class T>
  12. struct iterator_traits<const T*>{
  13. typedef typename T value_type;
  14. };

意义是:如果I有自己内部定义的value_type,那么通过这个 traits 的过程,萃取出来的value_type就是I::value_type。于是之前的函数func()就可以写成这样:
image.png
traits 可以方便地榨取迭代器的各种特性:
image.png

最常用到的迭代器相应特性有五种:value_typedifference_typepointerreferenceiterator catagory,一般要求容器的迭代器定义这五种相应型别:

  1. template<class I>
  2. struct iterator_traits{
  3. typedef typename I::iterator_category iterator_category;
  4. typedef typename I::value_type value_type;
  5. typedef typename I::difference_type difference_type;
  6. typedef typename I::pointer pointer;
  7. typedef typename I::reference reference;
  8. };

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

  1. template<class I>
  2. struct iterator_traits{
  3. ...
  4. typedef typename I::difference_type difference_type;
  5. };
  6. //针对原生指针的偏特化版本
  7. template<class T>
  8. struct iterator_traits<T*>{
  9. ...
  10. typedef ptrdiff_t difference_type;
  11. };
  12. //针对原生的pointer-to-const偏特化版本
  13. template<class T>
  14. struct iterator_traits<const T*>{
  15. ...
  16. typedef ptrdiff_t difference_type;
  17. };
  18. //自此,在需要任何迭代器I的difference_type时,可以这么写:
  19. 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 如下,每个新设计的迭代器都应继承自它:

  1. template<
  2. class Category,
  3. class T,
  4. class Distance = std::ptrdiff_t,
  5. class Pointer = T*,
  6. class Reference = T&>
  7. struct iterator{
  8. typedef Category iterator_category;
  9. typedef T value_type;
  10. typedef Distance difference_type;
  11. typedef Pointer pointer;
  12. typedef Reference reference;
  13. };

image.png

3.7 type_traits

TODO…