Loading

《Effective C++》再次探索traits技法

首先介绍C++标准程序库中的五种迭代器,关于这个可以看我的另一个笔记:http://blog.csdn.net/m0_37316917/article/details/70053513
对于这五种分类,C++标准程序库分别提供专属的标志结构加以确认:

struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag{}:public input_iterator_tag{};
struct bidirectional_tag:public forward_iterator_tag{};
struct random_access_iterator_tag:public bidirectional_iterator_tag{};

这些struct之间的继承关系是有效的is-a的挂系,所有forward迭代器都是input迭代器,以此类推。
traits并不是C++关键字或者一个预先定义好的构件,它们是一种技术,也是C++程序员共同遵守的协议,这个技术的要求之一是,它对内置类型和用户自定义类型的表现必须一样好,比如下面的advance函数:

template<typename IterT,typename DistT>
void advance(IterT&iter,Dist d)
{
    if(iter is a random access iterator)
    {
    iter+=d;
    }
    else
    {
    if(d>=0)
    {while(d--)++iter;}
    else
    {while(d++)--iter;}
    }
}

advance()如果收到的实参是一个指针(例如const char*)和一个int,上述advance仍然必须有效运作,那意味着traits技术也必须能够施行于内置类型比如指针身上。
“traits必须能够施行与内置类型”意味着“类型内的嵌套信息(nesting information)”这种东西出局了,因为我们无法将信息嵌套于原始指针中国,因此类型的traits信息必须位于类型自身之外,标准技术是将它放进一个template及其一个或者多个特化版本中,这样的templates在标准程序库中有多个,其中针对迭代器者被命名为iterator_traits:

template<typename IterT>
struct iterator_traits;//迭代器分类的相关信息

如你所限,iterator_traits是个struct,西瓜上traits纵使被实现为struct,但它们却又被成为traits classes。
iterator_traits的运作方式是,针对每一个类型IterT,在struct iterator_traits内一定声明某个typedef名为iterator_category,这个typedef用来确认IterT的迭代器分类。
为了支持内置类型指针迭代器。Iterator_traits特别针对指针类型提供一个偏特化版本,由于指针的行为和random access迭代器累死,所以iterator_traits为指针指定的迭代器类型是:

template<typename IterT>
struct iterator_traits<IterT*>
{
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)==typeif(std::random_access_iterator_tag))
    ......;//do something
}

但是这种方法将会导致编译问题,IterT的类型在编译期间获得,所以iterator_traits::iterator::category也可在编译期间确定,但if语句是在运行期才会核定,为什么将可在编译期完成的事情延迟到运行期才完成呢?这不仅浪费实践,也造成可执行代码的膨胀。
我们需要一个办法在编译期期间核定类型成功,这个办法就是重载:

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("Nagetive distance");

    while(d--)++iter;
}

由于forward_iterator_tag继承自input_iterator_tag,所以上述doadvance的input_iterator_tag版本也能够处理forward迭代器,这是iterator_tag structs继承关系带来的一项红利,实际上这也是public继承带来的部分好处:针对base class编写的代码用于derived class也行得通。
有了这些doadvance重载版本,advance需要做的只是调用它们并且额外传递一个对象,后者必须带有适当的迭代器分类,于是编译期运用重载解析几只调用适当的实现代码:

template<typename IterT,typename DistT>
void advance(IterT&iter,DistT d)
{
    doadvance(iter,d,typename std::iterator_traits<IterT>::iterator_category());
}
posted @ 2017-04-11 15:22  李正浩  阅读(150)  评论(0编辑  收藏  举报