STL源码剖析(三):迭代器与traits编程技法

为了使迭代器不暴露容器的成员及函数,一般将迭代器的开发工作交由容器的设计者,因此,STL每一种STL容器都提供专属的迭代器。

迭代器类型识别

C++不支持typeof(),即便动用RTTI性质中的typeid(),获得的也只是类型名称,不能拿来声明变量。

解决办法一:利用function template的参数推导机制

转调的妙用主要在参数的变化上

  • __uninitialized_copy转调__uninitialized_copy_aux:新增一个参数判断是否含有 non-triaval construct
  •  func() 转调func_impl():新增一个迭代器的解引用参数,用来推导迭代器所指对象的类型。

这种方法的缺陷:

  • 参数推导机制能得到迭代器所示元素类型value_type,但迭代器里面一共有5种内型,其他类型没办法通过解引用的方式推导。
  • 如果value type要做为返回值,这种方法也不能做到

解决办法二:迭代器内嵌类型声明

 这种方法在class里声明类型,但并不是所有迭代器都是class type,比如原生指针。

解决办法三:模板偏特化

也就是把指针、迭代器(可以看成智能指针)的特性用一个类提取,然后对这个提取类进行偏特化。

普通迭代器:

按道理,原生指针用这种方案也是正确的,但是,原始指针并没有 value_type啊,所以对原生指针进行偏特化,原生指针虽然没有value type,但提取特征时,能够读取到由它推导的value type。(这算是功能解耦的一大好处吧) 

查漏补缺:当原生指针类型为const int* 时,其指向的元素类型应该判断为int,以方便我们做 non-triaval之类的判断,但现在,它判断为 const int了,对此,还需设计另一个特化版本:

事实上,一个类型T被特化时,其const T往往也需要特化。

总结:traits是一个特性提取器

 五种迭代器类型

上面讲完了value type怎么推导的,接下来看看迭代器的其他四种类型怎么推导

 difference type

用来表示两个迭代器之间的距离,因此可以用来表示一个容器的最大容量。

这个type有啥用呢:如果一个泛型算法提供计数功能,如STL的count(),其传回值就必须使用迭代器的difference type。

测试用例:

int main(int argc, char const *argv[]) {
    vector<int> src;
    src.push_back(1);
    src.push_back(2);
    src.push_back(3);
    vector<int>::iterator i1 = src.begin();
    vector<int>::iterator i2 = src.end();

    vector<int>::difference_type diff = i2 - i1;
    cout << "diff = " << diff << endl;
    return 0;
}

结果diff=3 

针对原生指针(如vector的迭代器就是指向value type的原生指针),以C++内建的ptrdiff_t做为原生指针的difference type 。

reference type 和 pointer type

没啥好讲的,注意对原生指针以及对应的const版本做偏特化就行

迭代器类型iterator_category 

 直线与箭头代表的不是C++的继承关系,但可以当作继承关系,后面有讲继承的妙用。

以advance()为例,对于vector 迭代器(RAI),向前跳跃n个单位的时间复杂度是O(1),而对于list 迭代器(BI),向前跳跃n个单位的时间复杂度是O(n),因此,advance应根据不同的迭代器类型,做不同的向前跳跃操作:

但是这样有两个缺点:

  • 运行时期才决定执行的函数,影响执行效率,最好在编译期间就决定。
  • 如果迭代器类型又多了一种,这个函数也得改。if else不易于扩展

解决办法:前面讲value type不是可以进行类型推导,可以灵活绑定类型吗,同样,利用traits的思想,可以解决 iterator_category 面临的两个问题。

一、首先,将这5中类别定义为5个类。不用数字1,2,3,4,5,因为不方便类别扩展,也不方便做类型推导。也因为这些类只做类型推导,所以不需要任何成员,继承时不会造成额外开销(通过继承可实现传递调用:子类 FI 可以不用申明advance(),用父类 II 的就行)。

二、给advance添加一个类型推导参数,通过转调形成重载。

 

 三、调用,主要就是推导__advance的第三个参数:类型。

这份工作当然就交给traits机制:

advance的转调用其实是__advance(i,n,iterator_category(i))

iterator_category()源代码如下:

// 这里的参数其实就是__advance的第一个参数i
// 这里可以直接得到迭代器的iterator_category,转调的原因是因为要符合STL标准
template <class _Iter>
inline typename iterator_traits<_Iter>::iterator_category
iterator_category(const _Iter& __i) { return __iterator_category(__i); }

// 提取特定迭代器的iterator_category
template <class _Iter>
inline typename iterator_traits<_Iter>::iterator_category
__iterator_category(const _Iter&)
{
  typedef typename iterator_traits<_Iter>::iterator_category _Category;
  return _Category();
}

// 特定迭代器(以RAI为例),返回自己的类型,进而找到对应的重载函数
template <class _Tp>
inline random_access_iterator_tag iterator_category(const _Tp*)
  { return random_access_iterator_tag(); }

四、traits类增加iterator_category成员。 至此,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;
};

SGI STL的私房菜: __type_traits

<stl_iterator_base.h>中的iterator_traits负责提取迭代器特性,说的都是推导迭代器的相关类型,而traits的思想,被SGI用到了更广泛的地方。

<type_traits.h> 这个文件是SCI独有的,不在STL标准之内。

__type_traits负责提取类型特性:比如一个类是否含有non-trivial 函数,是否是POD类型。有趣的是,类似 iterator_category(有5种取值)的提取,__type_traits将其5个成员的两种取值(yes、no)也申明为类了,方便做类型推导。

struct __true_type {
};

struct __false_type {
};

template <class _Tp>
struct __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_assignment_operator;
   typedef __false_type    has_trivial_destructor;
   typedef __false_type    is_POD_type;
};

默认采用最严格的方式,全部设置为false,并针对不同的类,做了偏特化

__STL_TEMPLATE_NULL struct __type_traits<char> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值