简述STL之萃取器

萃取器相当于对迭代器的功能扩展,有了它让我们得以通过迭代器获得迭代器所指的数据的类型。

现在假设我们要实现一个算法,它接受一个迭代器作为参数,算法中需要定义一个变量,类型为迭代器所指的数据的类型。

对于这个问题,我们并没有什么好办法,C++没有typeof()这种东西,typeid()也只能获得关于类型的信息,并不能用来声明变量。

不过我们还是可以仰仗function templete的参数推导来完成这个任务。

template<class I,class T>
void func_impl(I iter,T t)
{
    T tmp;
    //do something
}

template<class I>
void func(I iter)
{
    func_impl(iter,*iter);
}

int main ()
{
    int i;
    func(&i);
}

把func()当成一个接口,把实际要做的工作放在func_impl中去做,编译器自动帮我们推导出了T,解决了问题。

可是这种方法有很大的缺陷,首先它很麻烦,其次如果我要把T类型当成函数的返回值,无论怎样这种方法都是没法解决问题的,我们只能另寻出路。

新的方法是在迭代器的定义中做手脚。

template<class T>
struct MyIter
{
    typedef T value_type;
    T* ptr;
    //...
};

template <class I>
typename I::value_type func(I ite)
{
    return *ite;
};

在迭代器的定义里加了一行typedef问题就解决了,函数的返回值被设置为typename I::value_type,加typename的原因是告诉编译器value_type是一个类型的名字,否则无法通过编译。

即便这样,问题还是没有完全解决,这种方法只适用于迭代器是一个class的情况下,我们可以在其中typedef,可是如果迭代器是原生指针,我们无法在原生指针中定义什么东西,不过我们可以引入一个中间层,达到我们的目的。

template<class Iterator>
struct iterator_traits
{
    typedef Iterator::value_type value_type;
};

template<class T>
struct iterator_traits<T*>
{
    typedef T value_type;
};

template<class T>
struct iterator_traits<const T*>
{
    typedef T value_type;
};
template<class I>
typename iterator_traits<I>::value_type func(I iter)
{
    return *ite;
}

这便是萃取器。对于原生指针,它提供了一个偏特化版本,即当T是原生指针的时候,调用的版本,对他进行了不同的操作,这样使用的时候就不用在乎迭代器是原生指针,还是class了,同理还有const类型的原生指针。

当然,我们要推导的不单单是value_type,还有pointer,reference,differenct_type,iterator_category所以萃取器的完整版是这样的。

template<class I>
struct iterator_traits
{
    typedef typename I::itreator_category    iterator_category;
    typedef typename I::value_type           valuetype;
    typedef typename I::difference_type      difference_type;
    typedef typename I::pointer              pointer;
    typedef typename I::reference            reference;
};

这里解释一下difference_type和iterator_category。

differece type用来表示两个迭代器之间的距离,也可以用来表示一个容器的最大容量,类型一般用内置的ptrdiff_t定义域cstddef,它存在的原因和size_t差不多,虽然底部可能是int或者是uint,不过为了可移植性,所以统一typedef为ptrdiff_t。

iterator_category是迭代器的类型,它的存在相当于告诉了编译器这个迭代器能做到什么功能。

具体的类型有五种

  1. input iterator 这种迭代器只能读,只能一个一个向前进
  2. output iterator 只能写,也只能一个一个前进
  3. forward iterator 可以读可以写,只能前进
  4. bidirectional iterator 可以读可以写,可以双向移动,不过一次还是只能移动一个单位
  5. random access iterator 可以读可以写,而且可以进行算数能力,例如+n,-n,<,>等操作

STL中的算法对于所获得的迭代器类型会有不同的做法,比如advanced(),它有两个参数,一个迭代器,一个数值n,他让迭代器向前移动n次。显然不同的迭代器我们不能一视同仁,必须要加以区分。来看看STL的做法。

template<class InputIterator,class Distance>
inline void advancd(InputIterator& i,Distance n)
{
    __advance(i,n,iterator_traits<InputIterator>::iterator_category() );
}

这样就根据iterator_category的具体类型生成了一个临时对象,从而转而去调用各自重载的__advance函数。

猜你喜欢

转载自blog.csdn.net/qq_33113661/article/details/88762810
今日推荐