STL之迭代器及traits编程

    不论是泛型思维或STL的实际运用,迭代器都扮演着重要的角色。STL的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶着剂将他们撮合在一起,这就是iterator!iterator是一种行为类似指针的对象,而指针的各种行为中最常见也最重要的便是内容提领(dereference) 和成员访问(member access),因此,迭代器最重要的编程工作就是对 operator* 和 operator-> 进行重载工作。

    迭代器相应型别获取的解决方法:利用function template的参数推导(argument deducation)机制,编译器会自动进行模板参数的推导。
template <class I, class T>
void func_impl(I iter, T t){
    T tmp; // 这里T就是迭代器所指之物的型别
    // ... 这里做原本func 应该做的全部工作
}
 
template <class I>
inline
void func(I iter){
    func_impl(iter, *iter); //func的工作全部移往func_impl
}
 
int main()
{
    int i;
    func(&i);
}
    Partial Specialization(偏特化):如果class template拥有一个以上的template 参数,我们可以针对其中某个(数个,但非全部)template 参数进行特化工作。偏特化是提供另一份template定义式,而其本身仍为templatized 。例子,面对一下这么一个类模板:
template <typename T>
class C {...};
我们很容易接受它有一个形式的如下的partial specialization:
template <typename T>
class C<T*> {...};
这个特化版本仅适用于“T为原生指针”的情况。有了这项利器,我们可以针对“迭代器模板参数为指针者”,设计特化版本的迭代器。
template <class I>
struct iterator_traits{ // traits意为“特性”
    typedef typename I::value_type value_type; // I内部定义有自己的value type
};
通过这个traits的作用,萃取出来的value_type就是I::value_type。则我们就可以这样定义函数返回型别:
template <class I>
typename iterator_traits<I>::value_type
func(I ite){ return *ite; }
};
    下面是一个迭代器为原生指针的偏特化版本:
template <class T>
typename iterator_traits<T*>{
    typedef T value_type;
};
    但是,针对“指向常数对象的指针”, iterator_traits<const int*>::value_type 获得的是const int,我们用它声明一个无法赋值的暂时变量也没什么用。因此,如果一个迭代器是一个pointer_to_const,我们应该设法令其value type为一个 non-const型别:
template <class T>
struct iterator_traits<const T*>{
    typedef T value_type;
};
    根据经验,最常用到的迭代器相应型别有五种:value type, difference type, pointer, reference, iterator catagoly。
template <class I>
struct iterator_traits{
    typedef typename I::value_type value_type;
    typedef typename I::iterator_category  iterator_category;
    typedef typename I::difference_type  difference_type;
    typedef typename I::pointer pointer;
    typedef typename I::reference reference;
};
iterator_traits必须针对传入之型别 pointer 及 pointer-to-const 者,设计特化版本。

    迭代器相应型别之一:value type
所谓value type是指迭代器所指对象的型别。任何一个打算与STL算法有完美搭配的class,都应该定义自己的value type内嵌型别。该型别做法如上所述。

    迭代器相应型别之二:difference type
difference type用来表示两个迭代器之间的距离,因此它可以用来表示一个容器的最大容量。如果一个泛型算法提供计数功能,其传回值就必须使用迭代器的 difference type:
template<class I, class T>
typename iterator_traits<I>::difference_type  //返回型别
count(I first, I last, const T& value){
    typename iterator_traits<I>::difference_type n = 0;
    for( ; first != last; ++first)
        if(*first == value) ++n;
    return n;
}
    针对相应型别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;
};
//原生指针的 point-to-const 偏特化版本 
template <class T>
struct iterator_traits<const T*>{
    typedef ptrdiff_t difference_type;
};
现在,任何时候当我们需要任何迭代器I的difference type,可以这么写:
typename iterator_traits<I>::diffenence_type

    迭代器相应型别之三:reference type
从迭代器所指之物的内容是否允许改变的角度观之,迭代器分为两种:不允许改变“所指对象之内容”者,称为constant iterators;允许改变“所指对象之内容”者,称为mutable iterators。当我们对一个mutable iteraors进行提领操作时,获得的不应该是一个右值,而应该是一个左值。该型别和下一个pointer类似,具体实现见pointer实现。

    迭代器相应型别之四:pointer type
template <class I>
struct iterator_traits{
    typedef typename I::pointer pointer;
    typedef typename I::reference reference;
};
//原生指针的偏特化版本
template <class T>
struct iterator_traits<T*>{
    typedef T* pointer;
    typedef T& reference;
};
//原生指针的 point-to-const 偏特化版本 
template <class T>
struct iterator_traits<const T*>{
    typedef const T* pointer;
    typedef const T& reference;
};
    迭代器相应型别之五:iterator_category
根据移动特性及施行操作,迭代器被分为五类:
  • Input Iterator:这种迭代器所指的对象,不允许外界改变。只读!
  • Output Iterator:只写!
  • Forward Iterator:允许“写入型”算法在此种迭代器所形成的区间上进行读写操作
  • Bidirectional Iterator:可双向移动
  • Random Access Iterator:前三种支持operator++,第四种再加上operator--;这种则涵盖所有指针算术能力,包括p+n, p-n, p[n], p1-p2, p1<p2
这五种迭代器的分类与从属关系(非继承,只是概念和强化)如下:

定义五个classes,代表五种迭代器类型:
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag : public input_iterator_tag{};
struct bidirectional_tag : public forward_iterator_tag{};
struct random_access_iterator_tag : public bidirectional_iterator_tag{};
这样我们可以做以下测试:
#include <iostream>
struct A{};
struct B1 : public A {};
struct B2 : public A {};

template<class I>
void
func(I& p, A){std::cout << "A version" << std::endl;};

template<class I>
void
func(I& p, B2){std::cout << "B2 version" << std::endl;};

int main()
{
int * p;

func(p, A());
func(p, B1());
func(p, B2());

return 0;
}
输出结果如下:

我们可以利用这种继承关系,对每一种迭代器类型设计不同的算法。

为了符合规范,任何迭代器都应该提供五个内嵌相应型别,以利于traits萃取,否则便是自别于整个STL架构,可能无法与其他STL组件顺利搭配。STL提供了一个iterator class,如果每个新设计的迭代器都继承自它,就可保证符合STL所需之规范:
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;
};
iterator class 不含任何成员,纯粹是型别定义,所以继承它没有额外负担;由于后三个参数都有默认值,故新的迭代器只需提供前两个参数即可
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值