背景
在STL迭代器中,为了适配不同类型的容器,常常需要迭代器主动获取 迭代器指向的类型,并且申明该类型的变量。模板函数的参数推导功能可用于推导变量类型
1 | template <class IT> |
可以看出,通过加一层函数模板的封装,可以推导出迭代器所指对象类型并定义变量,然而如果想要函数返回迭代器所指类型的变量却不可行,因为在调用这个函数模板前,我们并不能得到返回对象的类型,因此无法确定func
的返回类型。
在func
中,我们可以通过模板参数推导立即得到迭代器的类型,如果可以通过迭代器直接获取它所指对象的类型,那么便能达成我们的目的。内嵌型别声明
可以完成这一任务
1 | template <class T> |
那么现在只要迭代器在内部定义了value_type
,那么就能通过模板推导出其指向类型。但是有个问题是原生指针并未定义该type
,那么传入原生指针的时候,该函数就失效了。模板分为函数模板和类模板,它们都可以进行偏特化,先看看一个直接的做法,直接对该函数进行特化
1 | template <class T> struct Iter { |
这样该函数传入原生指针也能够正常工作了,但是缺点是如果func
代码量很长,就会多出两个影响阅读的函数代码。而且不够泛型化,要求之后所有基于该迭代器的函数都要提供两个特化版本,降低开发效率。与之前类似,可以对提取类型这件事加一层封装,让这层封装来处理这两个特化版本,后续函数调用该封装就不用自己特化。于是定义专门用于类型提取的类模板:
1 | template <class T> struct Iter { |
category
算法在使用不同迭代器的时候,应该有能力根据不同迭代器类型选择不同的方案,因为可以随机访问的迭代器往往比挨个访问要快。那么就要求我们能够提取出迭代器本身的类型,那么可用同样的方法——在迭代器内部定义自己的类型,这个类型往往是人为分类的,也需要事先定义出来,不过是一个空类,只用于类型区分:
1 | struct input_iterator_tag {}; |
定义好用于区分的类后,在自己的迭代器类中进行内嵌型别声明,然后就可以通过iterator_traits
提取类型,并且为了避免在运行时判断是哪个类型再去调用,可以通过函数重载实现该静态多态,于是需要再添加一层封装。
在实现自己的迭代器时,我们可以手动给每类iter都声明category
1 | template <typename T> struct InputIter { |
!!
但是这样容易忘记,且增加代码量,因此可以实现一个迭代器基类,在其中包含关于category
的模板参数,然后真正的迭代器在继承它的时候,特化这个参数即可,该基类一般定义如下
1 | template<class Category, class T, class Difference = ptrdiff_t, class Pointer =T*,class Reference = T&> struct iterator |
继承时特化该 类别参数
1 | template <typename T> struct InputIter : public iterator<input_iterator_tag, T> { |
1 | template <typename T> struct iterator_traits { |