traits萃取器 前面我们分析了迭代器的五类, 而迭代器所指向对象的型别被称为value type. 传入参数的类型可以通过编译器自行推断出来, 但是如果是函数的返回值的话, 就无法通过value type让编译器自行推断出来了. 而traits就解决了函数返回值类型. 同样原生指针不能内嵌型别声明,所以内嵌型别在这里不适用, 迭代器无法表示原生指针(int *, char *等称为原生指针). 这个问题就通过traits偏特化技术解决的. 这一篇我们就主要探讨traits是怎么实现这些没有能解决的问题.
iterator_traits结构 iterator_traits结构体就是使用typename对参数类型的提取(萃取), 并且对参数类型在进行一次命名, 看上去对参数类型的使用有了一层间接性. 以下就是它的定义.
1 2 3 4 5 6 7 8 template <class 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; };
在五类迭代器对模板对象的类型重新定义一次. 这里提取(萃取)出来的参数类型名都是统一的, 也就说明每个要使用traits编程的类必须以此类型名为标准, 而且需要自己对类定义这些类型名.
上面的traits结构体并没有对原生指针做处理, 所以还要为特化, 偏特化版本(即原生指针)做统一. 以下便是iterator_traits 的特化和偏特化实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 template <class T >struct iterator_traits <T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T& reference; }; template <class T >struct iterator_traits <const T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef const T* pointer; typedef const T& reference; };
这样不管是函数返回值类型还是原生指针都能通过萃取器萃取出来, typename I::类型进行类型萃取.
前面也分析了一下iterator_category函数, 现在再来看一下就能明白, 该函数是通过iterator_traits萃取的类型的iterrator_category确定该迭代器的类型的, 五类迭代器都设置了不同的iterator_category的值, 最后调用category()函数确定传入参数的类型.
1 2 3 4 5 6 7 8 9 10 11 12 template <class Iterator >inline typename iterator_traits<Iterator>::iterator_category iterator_category (const Iterator&) { typedef typename iterator_traits<Iterator>::iterator_category category; return category (); } 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 {};
继续看distance函数, __distance接受的前三个参数都是一样, 唯一不一样的就是最后一个参数, 通过iterator_category函数萃取出迭代器的类型从而根据类型而执行其对应的__distance函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 template <class InputIterator , class Distance >inline void distance (InputIterator first, InputIterator last, Distance& n) { __distance(first, last, n, iterator_category (first)); } template <class InputIterator , class Distance >inline void __distance(InputIterator first, InputIterator last, Distance& n, input_iterator_tag) { while (first != last) { ++first; ++n; } } template <class RandomAccessIterator , class Distance >inline void __distance(RandomAccessIterator first, RandomAccessIterator last, Distance& n, random_access_iterator_tag) { n += last - first; }
这里又列出了两个类型的实现, 这里用到了0可以转换成指针的性质, 相当于返回一个空指针, 但是可以通过它们确定不同的参数类型 .
1 2 3 4 5 6 7 8 9 10 11 template <class Iterator >inline typename iterator_traits<Iterator>::difference_type* distance_type (const Iterator&) { return static_cast <typename iterator_traits<Iterator>::difference_type*>(0 ); } template <class Iterator >inline typename iterator_traits<Iterator>::value_type* value_type (const Iterator&) { return static_cast <typename iterator_traits<Iterator>::value_type*>(0 ); }
value_type在空间配置器的时有提过, 就是关于destory的第二个版本.
1 2 3 4 5 6 template <class ForwardIterator >inline void destroy (ForwardIterator first, ForwardIterator last) { __destroy(first, last, value_type (first)); }
traits编程使用typename和特化, 偏特化将迭代器没能支持原生指针, 不能推导出函数返回值的问题完善了. 同时traits编程技法对迭代器加以规范, 提前知道了对象的类型相关信息, 从而选择最优的函数执行, 少了类型转化, 提高了执行效率.