迭代器与 traits 编程

最近在看侯捷的《STL 源码剖析》,对 traits 编程技巧有些个人看法。

 

traits 技巧是为了弥补 C++ 的语言缺陷的。假如我们自定义了一个迭代器:

// T 表示迭代器指向元素的类型
template <typename T>
class MyIterator {
     //....
}; 


接下来我们写了一个函数用于获得迭代器指向元素的值,我们可以这么写:

template <typename T>
T GetValue ( const MyIterator<T>& rhs ) {
    //...
}


但是这个函数不能适用于原生指针,这并不符合我们的期望,因为原生指针也是一种迭代器。

我们期待的应该是这样一种函数:

template <typename Iterator>
// value_type 表示 Iterator 指向元素的类型
typename value_type GetValue ( const Iterator& rhs ) {
    //...
}


为了适应原生指针,我们可以重载下函数:

// for native pointer
template <typename value_type>
value_type GetValue ( value_type* rhs ) {
    //...
}


对于原生常量指针,假如我们期待的返回值是一个变量,而不是一个 const 常量,则还要再做一个重载:

// for native native pointer to const
template <typeanem value_type>
value_type GetValue ( const value_type* rhs ) {
    //...
}

不需要可以忽略这一步

 

基于上面的假设,其实我们还要做第四个重载,保证当 MyIterator 指向元素的类型为常量时符合我们的期望,这里就不列出了

 

如此一来,通过函数重载,我们可以达到我们的预期

但是,假如我们还需要另一些迭代器的函数,例如 Swap, Sort, Copy...难道我们需要针对每一个函数做重载吗?这样一来,我们需要花费很多精力在函数的重载上。

为了解决这个问题,我们可以引入 traits 编程技术。

 

triaits 技术本质上是对迭代器做一层封装。这里我们定义一个中间层 MyTraits :

template <typename Iterator>
struct MyTraits {
    typedef typename Iterator::value_type value_type;
};

不过在这之前,我们做一个约定,我们约定迭代器里提供一个内嵌型别 value_type, 用于给 MyTraits 提供必要的信息:

    typedef T value_type;
修改下 MyIterator 模板:

template <typename T>
class MyIterator {
public:
    typedef T value_type; // 迭代器约定
    //...
};


然后针对原生指针,我们提供相应的 MyTraits 偏特化 ( template partial specialization ) :

// for native pointer
template <typename T>
struct MyIterator<T*> {
    typedef T value_type;
};

可能还要对常量指针做偏特化 ( 注意同上 ) :

// for native pointer to const
template <typename T>
struct MyIterator<const T*> {
    typedef T value_type;
};

 

如此一来,我们可以写适应于任意迭代器 ( 包括原生指针 ) 的函数了。改写下 GetValue:

template <typename Iterator>
typename MyIterator<Iterator>::value_type GetValue ( const Iterator& rhs ) {
    //...
}

这样我们通过不同于函数重载的另一种方法达到所需的预期了。

最后总结一下,为了算法能够同时适用于自定义迭代器与原生指针,我们有两种做法:

A. 同时提供参数为自定义指针和原生指针的两份函数模板

B. 提供一个 Traits 中间层以及针对原生指针的相应模板特化

// 注意在这里我们的代价是一个模板类 ( Traits ) 和编译时间的延长,但不用像方法A 一样花费大量时间在函数重载上

 

扩展一下,针对可能的需求,我们可以修改迭代器的约定和 Traits 的定义,提供新的信息,当然针对原生指针也需做相应处理

其中 STL 提供了一个迭代器模板,声明了迭代器的规范约定:

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;
};

我们的自定义迭代器可以直接继承这个模板

 

That's all,吃饭,(* ̄︶ ̄)y

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值