Fork me on GitHub

模版偏特化实现迭代器的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;
}

 

posted @ 2020-03-17 20:29  最后的战役aag  阅读(346)  评论(0编辑  收藏  举报