最近在看侯捷的《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