• 《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());
    }
    https://github.com/li-zheng-hao
  • 相关阅读:
    测试开发面试集锦_数据库
    测试开发面试集锦_linux
    测试开发面试题集锦_java
    Java中equals 和==的区别
    定时清理文件shell脚本
    java文件上传,upload使用
    python 获取错误日志,并发送邮件
    c语言代码审计规范
    渗透测试之nmap
    渗透测试之GoogleHack
  • 原文地址:https://www.cnblogs.com/lizhenghao126/p/11053741.html
Copyright © 2020-2023  润新知