SGISTL源码阅读六 迭代器下(traits编程技法)

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编程技法。
它增加了一层间接性,保证了原生指针也能获取到相应型别。

猜你喜欢

转载自blog.csdn.net/lyn_00/article/details/83989046