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_tag和random_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;
}