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