条款47:请使用traits classes表现类型信息(1)
Use traits classes for information about types.
今天我们讨论的这款内容涉及到一个STL实现上的一个关键技术traits技术,简单的说就是类的型别判定技术.
由于本条款要讨论内容比较多,所以我将分成两篇文章来表述,今天我们只讨论traits classes的实现,明天我将讨
论其使用,废话我就不多说了,我们现在就开始:
我们知道STL迭代器可分为五类,我再来简单的唠叨一下:input迭代器只能向前移动,一次一步,客户只可读取(
不能修改)它们所指的内容,而且只能读取一次;output迭代器情况类似,只是为了输出;以上这两种分别模仿文件的
读写指针,分类的代表为istream_iterators和ostream_iterators.它们两个属于能力最小的迭代器分类,只适合一
次性操作算法.第三类迭代器为forward迭代器,该种迭代器能够做上述两种类所能做的每一件事情,而且可以读写
所指物一次以上.第四类迭代器为bidirectional迭代器,比前一种威力更大,除了可以向前移动还可以向后移动.
STL的list,set,multiset,map,multimap的迭代器都是属于这一分类.最后一种分类也是威力最强的迭代器当属
random access迭代器,它可以在常量时间内向前或向后迢遥任意距离.vector,deque和string提供的迭代器就属于
这一分类.
对于这五种分类,C++标准库提供专门的类型标记结构对它们进行区分:
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag:public input_iterator_tag{};
struct bidirectional_iterator_tag:public forward_iterator_tag{};
struct random_access_iterator_tag:public bidirectional_iterator_tag{};
下面我们来看STL算法里面的函数advance的实现,其作用就是将某个迭代器移动某个距离:
template <typename IterT, typename DistT>
void advance(IterT& iter, DistT d);
为了对所有的迭代器分类都进行,我们最希望以下列方式进行实现:
template <typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if( iter is a random access iterator ){
iter += d; //针对random access迭代器使用迭代器算术运算
} else { //针对其它迭代器分类反复调用++或者--
if( d >= 0 ){
while( d-- )
++iter;
} else {
while( d++ )
--iter;
}
}
}
当写完上面的伪以后,我们就会立刻将焦点转移到一个点上:必须能够判断iter是否为random access迭代器
,也就是说需要知道类型IterT是否为random access迭代器分类.换句话说我们需要取得类型的某些信息.那就是
traits让你得以进行的事:它们允许你在编译期间取得某些类型信息.
Traits并不是C++关键字或一个预先定义好的构件;它们是一种技术,该技术的要求之一是,它对内置类型和用
户自定义类型的表现必须一样好.这就意味着'类型内的嵌套信息'这种东西出局了,因为我们无法将信息嵌套于
原始指针内.因此类型的traits信息必须位于类型自身之外.标识程序库的做法是把它放进一个template及其一个
或多个特化版本中.这样的templates在标准程序库中有若干个,其中针对迭代器者被命名为iterator_traits:
template<typename IterT>
struct iterator_traits;
iterator_traits以两个部分实现上述所言.首先它约定每一个用户自定义迭代器类型必须嵌套一个typedef,
名为iterator_category,用来确认类型信息.比如deque可随机访问,所以针对deque迭代器而设计的class看起来
会是这个样子:
template<...>
class deque{
public:
class iterator{
public:
typedef random_access_iterator_tag iterator_category;
...
};
...
};
list迭代器可双向行进,所以应该这样:
template<...>
class list{
public:
class iterator{
public:
typedef bidirectional_iterator_tag iterator_category;
...
};
...
};
对于iterator_traits只要响应iterator class的嵌套式typedef即可:
template <typename IterT>
struct iterator_traits{
typedef typename IterT::iterator_category iterator_category;
...
};
上面的对用户自定义类型行得通,但是仅仅这样的iterator_traits是不够的,你必须还要提供对指针(也是一种
迭代器)的支持,因为指针不可能嵌套typedef.于是我们就让iterator_traits的第二部分专门对付指针,它所利用的
工具就是模板的偏特化技术.由于指针的行径与random access迭代器类似,所以iterator_traits为指针指定的迭代
器类型如下:
template<typename IterT>
struct iterator_traits<IterT*>{
typedef random_access_iterator_tag iterator_category;
...
};
OK!现在你知道如何来实现一个traits class了,我们来大概简述一下如何设计并实现一个traits class:
■ 确认若干你希望将来可取得的类型信息.
■ 为该信息选一个名称(例如iterator_category)
■ 提供一个template和一组特化版本,内含你希望支持的类型相关信息.
好了,今天就到此为止,明天我们继续讨论.