条款47:请使用 traits classes 表现类型信息

Use traits classes for information about types.

STL 的 5 种迭代器分类

  • Input 迭代器:只能向前移动,一次一步,客户只可读取它们所指的东西,而且只能读取一次;

  • Output 迭代器:只能向前移动,一次一步,客户只可涂写它们所指的东西,而且只能涂写一次;

  • Forward 迭代器:可以做前两个迭代器的操作,而且可以读或写其所指物一次以上;

  • Bidirectional 迭代器:除了可以向前移动,还可以向后移动;

  • Random access 迭代器:可以在常量时间内向前或向后移动任意距离;

traits

traits 允许你在编译期间获取某些类型信息,例如 iterator_traits ,它的运作方式是针对每一个类型 IterT,在 structure iterator_traits<IterT> 内一定声明某个 typedef 名为 iterator_category的东西。

template<...>
class deque {
public:
    class iterator {
    public:
        typedef random_access_iterator_tag iterator_category;
        ...
    };
    ...
};

template<typename IterT>
class iterator_traits {
    typedef typename IterT::iterator_category iterator_category;
    ...
};

为了支持指针迭代器,iterator_traits 特别针对指针提供一个偏特化版本(partial template specialization):

template<typename IterT>
struct iterator_traits<IterT*> {
    typedef random_access_iterator_tag iterator_category;
    ...
};

而在实际使用过程中,可以通过 traits 在编译期间获取类型信息:

template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d) {
    if (typeid(typename std::iterator_traits<IterT>::iterator_category)
        == typeid(std::random_access_iterator_tag)) {
        ...
    }
}

不过这种使用方式存在问题,因为 IterT 类型在编译期间获知,所以 iterator_category 也可以在编译期间确定,但 if 语句却在运行期才会核定。这样不仅浪费时间,也造成可执行文件的膨胀。

如何使用 traits class

  • 建立一组重载函数或函数模板,彼此间的差异只在于各自的 traits 参数;

  • 建立一个控制函数或函数模板,它调用上述那些劳工函数并传递 traits class 所提供的信息;

template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d),
                std::random_access_iterator_tag) {                // 用于 random access 迭代器;
    iter += d;
}

template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d),
                std::bidirectional_iterator_tag) {                // 用于 bidirectional 迭代器;
    if (d >= 0) { while (d--) ++iter; }
    else { while (d++) --iter; }
}

template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d),
                std::input_iterator_tag) {                        // 用于 input 迭代器;
    if (d < 0) {
        throw std::out_of_range("Negative distance");
    } else { while (d--) ++iter; }
}

template<typename IterT, typename DistT>
void advance(Iter& iter, DistT d) {
    doAdvance(
        iter, d,
        typename
          std::iterator_traits<IterT>::iterator_category()
    );
}

Last updated

Was this helpful?