STL源码剖析第三章---迭代器与traits编程技巧

一、概述

  第二章中提到了__typr_traits(),在STL中它的作用是提取对象(可能是类,可能是指针或引用)的型别。第三章中对它用到的traits编程技巧进行了详细的介绍。首先从针对迭代器的特例iterator_traits入手,然后再延伸至可以提取任意对象型别的__type_traits。

  总结一下的话,__type_traits获取对象型别的特性是<strong>template参数推导</strong>、<strong>内嵌型别声明</strong>与<strong>template偏特化</strong>(针对指针和指向常量的指针)结合起来的产物。


二、迭代器

  STL的整体思想是将数据容器与算法分离开彼此独立设计,这两个独立体之间的粘合剂,就是迭代器。

  迭代器本质上是一种智能指针,参照C++标准对智能指针auto_ptr的定义,获取其所指对象的型别是必需的操作。这就要求我们在设计迭代器时必须设计一种机制,可以获得这一型别。于是iterator_traits就诞生了。(每个容器都有其特定的迭代器,这是封装性的一种体现,具体原因参考p84)


2.1 类对象值型别(value_type)的提取

  如何实现呢,对于类对象来说,其型别通过内嵌对象声明和template推导就可得到。


2.1.1 类对象value_type示例
  template <class T>
  struct MyIter{   //迭代器的设计,由容器设计者进行设计
      typedef T value_type;   //内嵌型别声明
      //...
  };
  template <class I>
  typename I::value_type   //调用时通过template参数推导可知返回的具体类型(必须加上typename声明它是一个型别而不是成员函数或是成员变量)
  func(I ite){   //算法的设计
  //...}


2.2 指针所指对象值型别(type_value)的提取

  内嵌型别声明+template参数推导可以得到类对象的型别,对于(原生)指针来说就不可取。因为这种方法得不到我们想要的指针指向对象的型别。利用偏特化机制可以做到这点。


2.2.1 指针value_type示例
  template <class T>
  struct MyIter{   //迭代器的设计,由容器设计者进行设计
      typedef T value_type;   //内嵌型别声明
      //...
  }

  template <class I>
  struct iterator_traits{    //为了利用偏特化特性,貌似"多此一举"声明的这个类是必需的
      typedef typename I::value_type;
  }

  template <class I>
  typename iterator_traits<I>::value_type   //实际iterator_traits情形
  func(I ite){   //算法的设计
  //...
  }

  //针对原生指针和指向常量的指针的偏特化
  template <class I>
  struct iterator_traits<I*>{   //提取所指对象的型别
      typedef I value_type;   
  }

  template <class I>
  struct iterator_traits<const I*>{   //提取所指对象的型别
      typedef I value_type;
  }


2.3 其他迭代器感兴趣的对象型别

  除了值型别(value_type)外,迭代器感兴趣的型别还有4种:iterator_type、difference_type、pointer与reference。见2.3.1节。


2.3.1 其他对象型别示例
  template <class I>
  struct iterator_traits{
      typedef typename I::iterator_category iterator_category;
      typedef typename I::value_type        value_type;
      typedef typename I::diference_type    difference_type;
      typedef typename I::pointer           pointer;
      typedef typename I::reference         reference;
  }

  //相应的偏特化版本
  template <class I>
  struct iterator_traits<T*>{
      //iterator_category比较特殊,见1.3.1节      
      typedef typename I::iterator_category iterator_category;  
   
      typedef I            value_type;
      typedef ptrdiff_t    difference_type;
      typedef I*           pointer;
      typedef I&           reference;
  }

  template <class I>
  struct iterator_traits<const T*>{
      //iterator_category比较特殊,见2.3.1节    
      typedef typename I::iterator_category iterator_category;

      typedef I            value_type;
      typedef ptrdiff_t    difference_type;
      typedef I*           pointer;
      typedef I&           reference;
  }


2.3.2 iterator_category型别
  迭代器自身是可以分成五类的,分类根据是迭代器对相应容器元素访问的能力强弱(例如FI比II能力强,FI包含II所有的访问方式,它们之间的关系可以考虑成继承关系)。
  因为迭代器是容器与算法的粘合剂,所以书中以advance()算法为例说明iterator_category的设计与实现:

迭代器分类

  //iterator父类
  template <class Category,
              class T,
              class Distance = ptrdiff_t,
              class Pointer = T*,
              class Reference = T&>
  struct iterator{
      typedef Category  iterator_category;
      typedef T         value_type;
      typedef Distance  difference_type;
      typedef Pointer   pointer;
      typedef Reference reference;
  }

  //iterator的每个具体实现都继承iterator父类
  template <class T>
  struct MyIter
  :public std::iterator<std::forward_iterator_tag, Item>
  {
      //...
  };

  //用于标记的型别
  struct input_iteartor_tag { };
  struct output_iteartor_tag { };
  struct forward_iteartor_tag : public input_iterator_tag{ };
  struct bidirectional_iteartor_tag : public forward_iterator_tag{ };
  struct random_access_iteartor_tag : public bidirectional_iterator_tag{ };

  template <class InputIterator, classs Distance>   //以算法可接受最低阶迭代器类型命名
  inline void advance(InputIterator& i, Distance n){
      __advance(i, n, 
                  iterator_traits<InputIterator>::iterator_category());   //临时对象
  }   

  template <class T>
  struct iterator_traits{
      typedef typename I::iterator_category iterator_category;
      //...
  };

  //针对原生指针的偏特化设计,原生指针是一种random access iterator
  template <calss T>
  struct iterator_traits<T*>{
      typedef random_access_iterator_tag iterator_category;
  }

  template <calss T>
  struct iterator_traits<cosnt T*>{
      typedef random_access_iterator_tag iterator_category;
  }
  //多个__advance()版本
  template <class RandomIterator, class Distance>
  inline void __advance(RandomIterator &i, Distance n, random_access_iterator_tag())
  { //... };

  template <class ForwardIterator, class Distance>
  template <class InputIterator, class Distance>
  //等等其他版本


三、__type_traits类型提取

  __type_traits类型提取,我们关注的是对象的trivial性质:它是否有trivial default ctor、copy ctor、assignment operator与dtor,以及它类型是否为POD类型。站在程序员的视角来判断这些性质是相对容易的:若class中含有指针成员,并对它进行了内存动态配置,那么这个class是需要实现它的non-trivial-xxx的。

  然而站在程序角度确实不同的,这就需要使用一种额外的机制来判断它的trivial性质,于是__type_traits横空出世了。


3.1 __type_traits示例

  struct __true_type {};
  struct __false_type {};

  //类模板
  template<class types>
  struct __type_traits{
      //为了处理编译器自身含有__type_traits定义这一情况
      typedef __true_type    this_dummy_member_must_be_first;

      typedef __false_type   has_trivial_default_constructor;
      typedef __false_type   has_trivial_copy_constructor;
      typedef __false_type   has_trivial_assianment_operator;
      typedef __false_type   has_trivial_destructor;
      typedef __false_type   is_POD_type;
  };

  //针对每一个支持类型的特化(char、int、long等等)
  template <>
  struct __type_traits<char>{

      typedef __true_type   has_trivial_default_constructor;
      typedef __true_type   has_trivial_copy_constructor;
      typedef __true_type   has_trivial_assianment_operator;
      typedef __true_type   has_trivial_destructor;
      typedef __true_type   is_POD_type;
  };

  //针对原生指针的偏特化

  template <class T>
  struct __type_traits<T*>{

      typedef __true_type   has_trivial_default_constructor;
      typedef __true_type   has_trivial_copy_constructor;
      typedef __true_type   has_trivial_assianment_operator;
      typedef __true_type   has_trivial_destructor;
      typedef __true_type   is_POD_type;
  };

3.2 以unintialized_fill_n为例来看__type_traits是如何使用的

  //获得迭代器指代对象的型别
  template <class Iterator>
  inline typename iterator_traits<Iterator>::valuetype*
  value_type(const iterator& i){
      return static_cast<typename iterator_traits<Iterator>::value_type*>(i);
  }

  template<class ForwardIterator, class Size, class T>
  inline ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, cnst T& x){
      return __uninitialized_fill_n(first, n, x, value_type(first));
  }

  template <class ForwardIterator, class Size, class T, class T1>
 inline ForwardIterator __uninitialized_fill_n(ForwardIterator first, Size n, const T& x, T1*){
      typedef typename __type_traits<T1>::is_POD_type   is_POD;
      return __uninitialized_fill_n_aux(first, n, x, is_POD());
  }

  //函数重载
  //若不为POD型,则不可避免的要使用default ctor、copy ctor、assignmant operator和dtor
  template <class ForwardIterator, class Size, class T>
  ForwardIterator
  __unintialized_fill_n_aux(ForwardIterator first, Size n, const T& x, __false_type){
      ForwardIterator cur = first;
      for(;n > 0; --n, ++cur)
          construct(&*cur, x);
      return cur;
  }

  //若不为POD型,则调用高阶fill_n(),使用malloc或memcpy来避免ctor、dtor的昂贵开销
  template <class ForwardIterator, class Size, class T>
  inline ForwardIterator
  __uninitialized_fill_n_aux(ForwardIterator forst, Size n, const T& x, __true_type){
      return fill_n(first, n, x);
  }

  template<class OutputIterator, class Size, class T>
  OutputIterator fill_n(OutputIterator first, Size n, const T& value){
      for(;n > 0; --n, ++first)
          *first = value;
      return first;
  }

注脚

这些读书笔记均为平时读书时随手记录下来的,之前一直分散在各处,特此将它们集中在一起,便于今后复习用。

转载于:https://www.cnblogs.com/ChyauAng/p/9705287.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值