SGISTL源码阅读六 迭代器下(traits编程技法)
前言
上一篇博客我们了解了迭代器的相应型别,简单的参数推导机制可以实现针对不同的类型进行不同的操作,但是这并不能解决所有的情况。本文章将要介绍_STL源代码门钥_——traits
编程技法。
凡是原生指针,都没有能力定义自己的相应型别,它们不是一种class type,traits
编程技法利用模板偏特化解决了这个问题。
声明内嵌型别
如果我们的相应型别需要作为返回值返回呢?就可以使用声明内嵌型别。
以下举例来自《STL源码剖析》P86
template <class T>
struct MyIter {
typedef T value_type; //内嵌型别声明
T* ptr;
Mylter(T* p = 0) : ptr(p) {}
T& operator*() const {return *ptr;}
//...
};
template <class I>
typename I::value_type //这一整行是func的返回值型别
func(I ite)
{
return *ite;
}
//...
MyIter<int> ite(new int(8));
cout << func(ite); //输出8
typename I::value_type
你可能会对这一行代码有一些疑惑,其实就是typename
的一个特殊用法,因为T是一个template
参数,在被编译之前,编译器对它一无所知,我们需要用typename
告诉编译器这是一个型别。(传送门:https://blog.csdn.net/lyn_00/article/details/83786232)
这个方式看起来似乎很不错,但是如果是原生指针呢?我们无法使用内嵌型别。举个例子来说明
template < class T>
class A {
typedef T value_type;
//...
};
template <class T>
class B {
typedef T value_type;
//...
};
我们的迭代器都是通过这种方式封装起来的,通过内嵌型别来获取相应型别typename I::value_type
,但是原生指针并不是一个类,::这种方式是类获取成员的。
模板偏特化
这部分内容博主已经进行了一些总结,建议在学习traits
编程技法之前先温故一下
(传送门:https://blog.csdn.net/lyn_00/article/details/83548629)
深入源码
//这样看来,traits其实就是多了一层间接性
//把在Iterator中定义过的再定义一次
//这样做的好处是可以让traits有多个特化版本,来解决原生指针的问题
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;
};
//偏特化版本一
//针对于原生指针(迭代器类型为random_access_iterator)
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;
};
//偏特化版本二
//如果传入的是const修饰的原生指针,按照版本一,我们获取到的是const int
//但是这并不是我们想要的,我们只是想要获取到他的类型而已(不带修饰符的那种)
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;
};
traits
的作用就如其名,无论是原生指针还是迭代器,通过“萃取”,我们都能获取到它的相应型别。
就是把原生指针和迭代器都交给traits去处理,对于迭代器来说是多了一层间接性,但是对于原生指针来说,就是用traits把它封装起来了。
这样原生指针也可以使用内嵌型别的方法获取相应型别了。
这部分内容可能比较难理解一些。
SGISTL其实就是利用traits
编程技法,考虑不同的情况,将它的效率提升至极致。我们从源码中来看。
//针对不同类型的迭代器进行了模板偏特化
//__(双底线前缀词)这种命名方式是指这是SGISTL内部所用的东西,不在STL标准范围之内
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;
}
template <class InputIterator, class Distance>
inline void distance(InputIterator first, InputIterator last, Distance& n) {
__distance(first, last, n, iterator_category(first));
}
//将相应型别作为返回值,使用声明内嵌型别。
//可利用traits萃取原生指针或迭代器的相应型别
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
__distance(InputIterator first, InputIterator last, input_iterator_tag) {
iterator_traits<InputIterator>::difference_type n = 0;
while (first != last) {
++first; ++n;
}
return n;
}
//原生指针是RandomAccessIterator的一种
template <class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last,
random_access_iterator_tag) {
return last - first;
}
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last) {
typedef typename iterator_traits<InputIterator>::iterator_category category;
return __distance(first, last, category());
}
总结
我们介绍了迭代器的traits
编程技法。
它增加了一层间接性,保证了原生指针也能获取到相应型别。