Effective C++ 条款47:请使用 traits classes 表现类型信息
traits classes 通过 templates 和 “templates 特化” 使得 “类型相关信息” 在编译期可用,通过重载技术(overloading)实现在编译期对类型执行 if…else 测试。
一、从一个实际问题出发
STL 中有一个非常有用的函数 advance,它的作用是将迭代器向前移动指定的距离:
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d); // 将迭代器向前移动 d 个单位
这个函数看似简单,但实现起来却暗藏玄机。因为 STL 中的迭代器分为多种类型,不同类型的迭代器支持的操作截然不同:
| 迭代器类型 | 能力 | 示例容器 |
|---|---|---|
| Input Iterator | 只读,单次遍历,只能前进 | istream_iterator |
| Output Iterator | 只写,单次遍历,只能前进 | ostream_iterator |
| Forward Iterator | 可读写,多次遍历,只能前进 | forward_list |
| Bidirectional Iterator | 可读写,多次遍历,可前进后退 | list, set, map |
| Random Access Iterator | 可读写,支持随机访问(迭代器算术) | vector, deque, array |
对于 Random Access Iterator,我们可以用 iter += d 在 O(1) 时间内完成移动;但对于 Input Iterator 或 Forward Iterator,我们只能一步一步地 ++iter,需要 O(n) 时间。
问题来了:如何在编译期知道一个迭代器属于哪种类型,从而选择最优的实现?
二、迭代器标签(Iterator Tags)
C++ 标准库为这五种迭代器类型定义了对应的标签结构(tag struct):
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 {
};
注意这里的继承关系!这种层次结构非常重要,它允许我们利用多态性来处理迭代器类型。
2.1 容器中的迭代器类型声明
每个 STL 容器的迭代器类都会内嵌一个 iterator_category 类型定义:
// deque 的迭代器是随机访问迭代器
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:类型信息的搬运工
3.1 为什么需要 traits?
你可能会想:直接在迭代器类里获取 iterator_category 不就行了?
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d) {
// 这样写有什么问题?
if (typeid(typename IterT::iterator_category) == typeid(random_access_iterator_tag)) {
iter += d; // 编译错误!对于非随机访问迭代器,iter += d 不合法
}
// ...
}
致命缺陷:
if判断在运行期执行,但两个分支在编译期都会被编译- 如果
IterT是list<int>::iterator,iter += d会导致编译失败 - 即使编译通过,运行期判断也带来不必要的开销
3.2 原生指针怎么办?
另一个关键问题:原生指针(如 int*)也是一种迭代器,但指针不是类,无法内嵌 typedef!
3.3 iterator_traits 登场
iterator_traits 是标准库提供的 traits class,它统一了"类迭代器"和"原生指针"的类型信息获取方式:
// 通用版本:针对类迭代器
template<typename IterT>
struct iterator_traits {
typedef typename IterT::iterator_category iterator_category;
typedef typename IterT::value_type value_type;
typedef typename IterT::difference_type difference_type;
typedef typename IterT::pointer pointer;
typedef typename IterT::reference reference;
};
// 偏特化版本:针对原生指针
template<typename T>
struct iterator_traits<T*> {


205

被折叠的 条评论
为什么被折叠?



