• 模版偏特化实现迭代器的traits技术


    1、什么是迭代器的traits技术

    在c++的标准库中,容器和算法是独立开发的,并用迭代器作为容器和算法沟通的桥梁。当某个容器使用算法时,算法必须要通过迭代器了解容器的某些信息,便于自己操作,例如容器中的元素类型等。为了实现算法更加方便的通过迭代器获取容器的信息,由此开发了迭代器的triats技术。

    2、什么是模版偏特化

    2.1 首先解释一下什么是模版的特化

    有两种情况需要使用模版的特化,第一种:对于某些类型,通用定义可能会导致编译失败或实现方法不对;第二种:对于某些类型,其有特殊的实现技巧,可以提高效率。当我们不能或不希望使用模版版本时,可以定义类或函数模版的一个特例化版本,特化之后的类或函数就不再是模版类或函数。下面以hash类为例:

     1 //hash 的泛化版本
     2 template<typename Key>
     3 struct hash {};
     4 
     5 //hash的三个特化版本
     6 template<>
     7 struct hash<char> {
     8     size_t operator()(char x) const {return x;}
     9 };
    10 
    11 template<>
    12 struct hash<int> {
    13     size_t operator()(int x) const {return x;}
    14 };
    15 
    16 template<>
    17 struct hash<long> {
    18     size_t operator()(long x) const {return x;}
    19 };

    2.2 模版偏特化

    偏特化只是针对类,偏特化有两个种类,一个是对于模版参数个数的偏特化

    1 //泛化版本
    2 template<typename U, typename V>
    3 class C {...};
    4 
    5 //模版参数个数偏特化
    6 template<typename V>
    7 class C<int, V> {...};

    另一种是对模版参数范围的偏特化

    1 //泛化版本
    2 template<typename T>
    3 class C {...};
    4 
    5 //模版参数范围的偏特化
    6 template<typename T>
    7 class C<T*> {...};

    对于偏特化版本的类或函数,其本质依然是类模版或函数模版。《泛型思维》中对偏特化这样定义“针对(任何)template参数更近一步的条件限制所设计出来的一个特化版本”。

    3、traits技术为什么需要偏特化

    通常来说,根据迭代器的typedef,算法完全可以获取他想要知道的关于容器的所有信息。但是,普通指针(例如int *)也是一种迭代器,若算法不支持c/c++语言中的数组,此举动十分不明智,但是普通指针并没有typedef这种定义。为了实现算法有效的支持数组,由此引进了一种中间技术:traits,侯捷老师称它为萃取机,意为把一种迭代器或是指针丢到萃取机中,从中获取想要的信息。

    4、traits技术的实现

    先看下面一个例子

    1 template<typename I>
    2 struct iterator_traits
    3 {
    4     typedef typename I::value_type value_type;
    5     ...
    6 }

    上面value_typ就是迭代器的特性之一。上面的代码意思是:如何类型I拥有自己定义的value_type,那么通过traits的作用,萃取出来的value_type就是I::value_type。

    例如:

    1 template<typename I>
    2 typename iterator_traits<I>::value_type
    3 fun()
    4 {
    5     ...;
    6 }

    上面类型I就可以通过iterator_traits就可以获取I自己定义的value_type,但是你肯定会问,我们下面这种做法也可以啊,为什么要多此一举

    1 template<typename I>
    2 typename I::value_type
    3 fun()
    4 {
    5     ...;
    6 }

    我们这样做的原因就是想对iter欧ator_traits写一些泛化版本来支持原生的数组,因为如果我们将int *作为上例的I,这样编译就不会通过,因为int *中根本不是自定义类型,也不会定义value_type。因此我们需要为iterator_traits写如下一个版本来实现对int *和const int *或其他内置类型作偏特化版本

     1 template<typename T>
     2 struct iterator_traits<T *>
     3 {
     4     typedef T value_type;
     5     ...
     6 }
     7 
     8 template<typename T>
     9 struct iterator_traits<const T *>
    10 {
    11     typedef T value_type;
    12     ...
    13 }

    但是请注意第二个指向常数对象的指针(pointer-to-const),为什么萃取指向常数对象的指针会得到非常数类型呢?因为我们萃取这些特性主要是用来定义一些变量,如何对const int *萃取出的是const int ,我们无法赋值,也就没什么用处。

    5、萃取机的完整实现

    template<typename Iterator>
    struct iterator_traits
    {
        typedef typename Iterator::iterator_category  iterator_category;
        typedef typename Iterator::value_type           value_type;
        typedef typename Iterator::difference_type    difference_type;
        typedef typename Iterator::pointer                pointer;
        typedef typename Iterator::reference             reference;
    }
    
    //针对原生指针而设计的偏特化版本
    template<typename T>
    struct iterator_traits<T*>
    {
        typedef typename random_access_iterator_tag  iterator_category;
        typedef typename T                                         value_type;
        typedef typename ptrdiaff_t                             difference_type;
        typedef typename T*                                       pointer;
        typedef typename T&                                       reference;
    }
    
    //针对原生指针的pointer-to-const而设计的偏特化版本
    template<typename T>
    struct iterator_traits<const T*>
    {
        typedef typename random_access_iterator_tag  iterator_category;
        typedef typename T                                         value_type;
        typedef typename ptrdiaff_t                             difference_type;
        typedef typename T*                                       pointer;
        typedef typename T&                                       reference;
    }
  • 相关阅读:
    使用递归输出某个目录下所有子目录和文件
    如何进行复杂度分析?
    什么是时间复杂度?什么是空间复杂度?
    什么是复杂度?为什么要进行复杂度分析?
    什么是递归?递归的优缺点是什么?
    Executor 如何使用?
    什么是spring boot?为什么要用?
    spring boot核心配置文件是什么?
    @Autowired的作用是什么?
    @RequestMapping的作用是什么?
  • 原文地址:https://www.cnblogs.com/swenwen/p/12500505.html
Copyright © 2020-2023  润新知