STL之traits技法

traits汉译为特性,形状,暂且知道这是于特性相关的就OK。

後捷老师说“源码面前,了无秘密”,所以逼人打算直面晦涩的源码,一探究竟。

现在,先来想象一个简单的场景,有一个函数advance(iterator,step),这个函数可以使迭代器iterator前进step个距离(step也可以为负),这里暂时不考虑step为负的情况

那么一个机器粗烈的实现可能是下面这样的

template <typename ITERATOR, typename DISTANCE>
void advance(ITERATOR &itor, DISTANCE step)
{
	while (step-- > 0)
		++itor;
}

这个实现极其简单,但是也极其不服和STL的效率精神。对于一个list来说,这种方式无可挑剔。但是对于一个vector来说,通常vector的迭代器就是一个原生指针,例如vector<int>的迭代器类型就是int*,并且vector多以数组方式实现,所以上面的advance明显没有对可以随机访问的迭代器做优化,怎么办?好说,写两套advance呗。

template <typename ITERATOR, typename DISTANCE>
void advance_for_list(ITERATOR &itor, DISTANCE step)
{
	while (step-- > 0)
		++itor;
}


template <typename ITERATOR, typename DISTANCE>
void advance_for_vector(ITERATOR &itor, DISTANCE step)
{
	itor += step;
}
看,多么痛的实现,使用者还必须知道目前的迭代器作用于vector还是list上,从而选用合适的advance变种,但是这样就把算法和容器强耦合了,更换了容器的话就必须得修改算法,对使用者来说太不友好了,怎么办。

说实话,没看STL源码之前,我也没想明白,STL源码如下:

template<typename _InputIterator, typename _Distance>
inline void __advance(_InputIterator& __i, _Distance __n, input_iterator_tag)
{
    while (__n--)
      ++__i;
}

template<typename _RandomAccessIterator, typename _Distance>
inline void __advance(_RandomAccessIterator& __i, _Distance __n, random_access_iterator_tag)
{
    __i += __n;
}

template<typename _InputIterator, typename _Distance>
inline void advance(_InputIterator& __i, _Distance __n)
{
  std::__advance(__i, __d, std::__iterator_category(__i));
}
我去除了一下与分析traits无关的东西,并调整了下格式,所以现在看起来清爽多了。

想一想我们的适用场合

  std::vector<int> ints;
  for (int i = 1; i < 9; ++i)
    ints.push_back(i);

  std::vector<int>::iterator itor = ints.begin();
  std::advance(itor, 2);
  std::cout << *itor << std::endl;
无论对于list还是vector,我们都是调用的advance,在advance内部,我们可以看到,这个函数转交给了另一个含有3个参数的__advance函数,其中第三个参数类型并不容易看出来,带有__开头的函数,我们把它叫做内部实现,最好不要调用它,我们要调用STL规定好的接口。

两个__advance函数的第三个参数类型分别为:input_iterator_tagrandom_access_iterator_tag

从实现上看,input_iterator_tag貌似是用于链表的,而random_access_iterator_tag貌似是用于vector的。但这到底有什么用呢,我们必须再看下这两个tag是怎么实现。

STL源码如下:

struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { };
struct random_access_iterator_tag : public bidirectional_iterator_tag { };
看起来好像更模糊了,是不是?

硬着头皮继续看看std::__iterator_category()是怎么实现的。

template<typename _Iterator>
    struct iterator_traits
    {
      typedef typename _Iterator::iterator_category iterator_category;
      typedef typename _Iterator::value_type        value_type;
      typedef typename _Iterator::difference_type   difference_type;
      typedef typename _Iterator::pointer           pointer;
      typedef typename _Iterator::reference         reference;
    };

  /// Partial specialization for pointer types.
  

template<typename _Tp>
    struct iterator_traits<_Tp*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef _Tp                         value_type;
      typedef ptrdiff_t                   difference_type;
      typedef _Tp*                        pointer;
      typedef _Tp&                        reference;
    };


/** * This function is not a part of the C++ standard but is syntactic * sugar for internal library use only. */ 
template<typename _Iter> 
inline typename iterator_traits<_Iter>::iterator_category __iterator_category(const _Iter&) 
{ return typename iterator_traits<_Iter>::iterator_category(); }

仔细分析发现对于一个迭代器itor来说,

typename iterator_traits<itor>::iterator_category
得到的是迭代器itor的某个类型属性,所以__iterator_category函数就相当于是定义了一个itor相关联的某个对象,而这个对象具有的类型就是input_iterator_tag或者random_access_iterator_tag.

从下面代码可以看出,

template<typename _Tp>
    struct iterator_traits<_Tp*>
    {
      typedef random_access_iterator_tag iterator_category;//注意这一行
      typedef _Tp                         value_type;
      typedef ptrdiff_t                   difference_type;
      typedef _Tp*                        pointer;
      typedef _Tp&                        reference;
    };
如果传入的是vector的迭代器,那么__iterator_category返回的将是一个random_access_iterator_tag类型的对象。我们在看看list的迭代器源码:

template<typename _Tp>
    struct _List_iterator
    {
      typedef _List_iterator<_Tp>                _Self;
      typedef _List_node<_Tp>                    _Node;

      typedef ptrdiff_t                          difference_type;
      typedef std::bidirectional_iterator_tag    iterator_category; //注意这一行
      typedef _Tp                                value_type;
      typedef _Tp*                               pointer;
      typedef _Tp&                               reference;
}
如果传入的是list的迭代器,那么__iterator_category返回的将是一个bidirectional_iterator_tag类型的对象,而这个类型间接继承与input_iterator_tag。

现在一切都清楚了(其实我发现自己描述的一塌糊涂)。这一次我们逆着来,看看advance怎样作用于list上的迭代器。

1:首先对于一个list的迭代器,我们已经确定的是

template<typename _Tp>
    struct _List_iterator
    {
      typedef std::input_iterator_tag    iterator_category; //注意这一行,我做了修改,更利于分析
    }
所以__iterator_category函数作用于list的迭代器上面将会得到一个std::input_iterator_tag类型的对象。
2:我们调用advance函数的时候,advance函数利用__iterator_category得到迭代器的一个tag对象,由上一步知道,这个tag对象的类型是std::input_iterator_tag

3:__advance函数构成了重载,而我们传入的第三个参数是std::input_iterator_tag类型,所以调用下面的这个实现。

template<typename _InputIterator, typename _Distance>
inline void __advance(_InputIterator& __i, _Distance __n, input_iterator_tag)
{
    while (__n--)
      ++__i;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值