Effective C++ Item 47 通过traits classes获取类型信息

一、advance函数介绍
template<typename IterT, typename DistT> 
void advance(IterT& iter, DistT d);

         advance函数是C++的一个库函数,该函数的功能是将迭代器向前或者向后移动n个位置。advance是一个函数模板。理论上,只要 iter+=d 就能实现迭代器的移动,但是只有随机访问迭代器才支持算数运算。那么库函数是怎么实现这个功能的呢。

        首先,回顾一下迭代器的类型。

二、迭代器类型

        共有5种迭代器类型:

        1、Input iterators (输入迭代器)—— 只能向前移动,每次只能移动一个元素,只能读取迭代器所指向的元素,并且只能读一次。c++标准库的istream_iterator就是这种迭代器。

        2、Output iterators(输出迭代器) —— 与Input iterators类似,但是这种迭代器用于写操作。ostream_iterators属于这种迭代器。

        3、forward iterators(前向迭代器)——具有Input iterators和Output iterators的所有功能。并且能够读写多次。单链表迭代器就是这种迭代器。

        4、Bidirectional iterators(双向迭代器)——具有forward iterators迭代器的所有功能。并且能够向后移动。双链表的迭代器就是这种迭代器。

        5、random access iterators(随机访问迭代器)——具有Bidirectional iterators迭代器的所有功能。并且支持算数运算。

三、advance的实现思路

        首先想到的是,不使用算数运算来实现,通过多次的自增运算符,来实现迭代器的移动,这样所有迭代器类型都支持自增操作,但是这样效率比较低。

        理想的实现应该如下:

template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
    if (iter == 随机访问迭代器) {
        iter += d; // use iterator arithmetic
    }    // for random access iters
    else {
        if (d >= 0) { while (d--) ++iter; } // use iterative calls to
        else { while (d++) --iter; } // ++ or -- for other
    } // iterator categories
}

        我们需要知道iter是不是随机访问的迭代器。这就是traits的功能,能够让我们在编译的时候获取类型信息。

        另外,advance还需要满足内置类型,比如移动指针。这样在自定义类型里面加入类型信息的方法也就行不通了,因为无法为内置类型增加信息。那么标准库是怎么实现的呢?

        首先,对于每种迭代器,标准库都定义了一个标签结构体。

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

        之后,每个迭代器必须包含一个typedef,来定义iterator_category为以上5种类型之一。

例如:

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

        针对迭代器,标准库定义了一个模板。用来获取迭代器类型信息。

template<typename IterT> // template for information about
struct iterator_traits; // iterator types

        iterator_traits对IterT::iterator_category又进行typedef定义。

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

        对于内置类型,有进行了模板偏特化(partial template specialization)。

template<typename IterT> // partial template specialization
struct iterator_traits<IterT*> // for built-in pointer types
{
    typedef random_access_iterator_tag iterator_category;
    ...
};

        基于以上信息,对advance进行重新定义如下:

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))
    {
        iter += d;    
    }
    ...
}

        但是上面的代码还是不能通过编译,主要是因为iter+=d这行代码并不是所有的迭代器都支持。并且,IterT的类型是在编译时就确定的,所以std::iterator_traits<IterT>::iterator_category可以在编译时候就确定。但是if语句是在运行时进行的判断。这是在浪费时间。

        标准库通过函数过载来实现在编译阶段实现if语句的功能。

        首先定义doAdvance模板函数。

template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // random access
std::random_access_iterator_tag) // iterators
{
    iter += d;
}
 
template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // bidirectional
std::bidirectional_iterator_tag) // iterators
{
    if (d >= 0) { while (d--) ++iter; }
    else { while (d++) --iter; }

} 

template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // input iterators
std::input_iterator_tag)
{
    if (d < 0 ) {
    throw std::out_of_range("Negative distance"); // see below
    } 
    while (d--) ++iter;
}

        最终定义advance模板函数如下:

template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
    doAdvance( // call the version
            iter, d, // of doAdvance
            typename // that is
            std::iterator_traits<IterT>::iterator_category() // appropriate for
            ); // iter's iterator
}
        四、总结

        1、通过模板和模板特例化,traits class实现在编译阶段获取类型信息。

        2、配合函数过载,能够实现编译阶段的if...else

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值