一、迭代器相应类型
- 前文介绍了迭代器的雏形,在使用迭代器的时候,我们可能需要知道这个迭代器所指之物的数据类型,但是C++不支持typeof()这种机制,因此我们需要一种技术来获取迭代器所指之物的类型
参数推导机制
- 参数推导机制可以让我们推断出参数的数据类型
template<class I,class T> //此例中,T为int void func_impl(I iter, T t) { T tmp; //... } template<class I> inline void func(I iter) { func_impl(iter, *iter); } int main() { int i; func(&i); }
- 迭代器相应类型不只是“迭代器所指对象的类型”一种而已。最常见的相应类型有5种,在后面介绍
二、声明内嵌类型
- 上面介绍了函数的参数推导机制,推导机制虽然好,但是只能获取参数的类型,不能获取函数返回值的类型
- 作为解决办法,声明内嵌类型是一个好主意,例如:
声明内嵌类型
- 下面我们在MyIter中使用typedef声明一个别名,这个别名就是传入迭代器的类型的别名
- 在func函数中,我们返回迭代器中的元素,因此我们需要知道迭代器所指之物的类型才能为其声明返回值类型,此处我们使用了typename来引出迭代器中的类型别名
//迭代器类 template<class T> struct MyIter { typedef T value_type; //内嵌类型别名,用value_type来表示迭代器类所指之物的类型 T* ptr; MyIter(T* p = 0) :ptr(p) {} T& operator*()const { return *ptr; } }; /* 为什么要使用typename:因为编译器不知道T是什么,也就是说编译器不知道MyIter<T>::value_type 是一个类型还是一个成员函数还是一个成员变量,此处我们使用typename告诉编译器这是一种类型, 这样才能顺利编译通过 */ template<class I> typename I::value_type func(I ite){ //typename I::value_type 为func的返回值类型 return *ite; } MyIter<int> ite(new int(8)); cout << func(ite);
三、偏特化
- 上面的声明内嵌类型很好用,但是并不是所有迭代器的都是class,例如原生指针就不是class(例如int*,stirng*),那么此时我们就无法使用内嵌类型了,但是STL算法、容器等都是接受原生指针作为一种迭代器的。那么针对于原生指针我们该如何处理呢?
- 是的,偏特化(Paritial Specialization)可以做到
偏特化
- 偏特化的概念是:我们可以针对类模板中的template参数进行特化工作。为其提供一个特化版本(也就是讲template中参数赋予明确的指定)
- 例如下面是常规的类模板,允许接收T为任何类型
template<typename T> class C{};
- 下面是一个特化版本,允许接收T为原生指针的情况
template<typename T> class C<T*>{};
为迭代器声明一个偏特化
- 在“二”中我们的迭代器没有偏特化版本,因此传入原生指针之后就不可以推导出迭代器所指之物的类型了
- 有了偏特化,我们就可以对迭代器进行偏特化,设计出下面的迭代器
//偏特化迭代器类 template<class T> struct MyIter<T*> { typedef T value_type; //这个迭代器接受原生指针,从而获得指针所指之物的数据类型 T* ptr; MyIter(T* p = 0) :ptr(p) {} T& operator*()const { return *ptr; } };
四、Traits编程技术
- 通过上面的介绍,我们正式开始介绍Traits编程技术
- 下面的类模板专门用来“萃取”迭代器的类型,value_type就是迭代器的特性之一:
//如果I为迭代器类,并且迭代器类中声明有类型别名,那么我们就可以萃取出其中的数据类型
template<class I>
struct iterator_traits {
typedef typename I::value_type value_type;
};
- traits是指:如果I定义有自己的value type,那么我们就可以通过上面设计的iterator_traits类来萃取出I的value_type(数据类型)。我们上面的func函数还可以更改为:
//萃取的使用场景
template<class I>
typename iterator_traits<I>::value_type func(I ite) { //typename iterator_traits<I>::value_type 为func的返回值类型
return *ite;
}
- traits也可以有特化版本:例如下面只针对于迭代器是原生指针的偏特化版本,其能萃取出指针所指之物的数据类型
template<class T>
struct iterator_traits<T*> {
typedef T value_type;
};
注意“指向常数对象的指针”特化本
- 针对“指向常数对象的指针”,下面的式子会得出什么结果呢?
iterator_traits<const int*>::value_type;
- 我们希望这种traits机制来获取迭代器的类型,但是如果上面的式子萃取出的是const int类型,那么声明就无法赋值了,因此如果迭代器是个pointer-to-const,我们应该设置下面的特化版本
template<class T> struct iterator_traits<const T*> { typedef T value_type; //萃取出的是T,而不是const T };
- 下图说明了traits所扮演的“特性萃取机”角色,萃取各个迭代器的特性
- 当然如果希望“特性萃取机”traits能够正常工作,前提是迭代器必须在自己的类中声明类型别名(typedef)
五、iterator_traits萃取机
- 下面是迭代器源码中的iterator_traits类(其中的一个版本),其用来萃取迭代器中的相应类型
//迭代器的源码
template<class Category,class T,class Distance=ptrdiff_t,
class Pointer=T*,class Reference=T&>
struct iterator {
typedef Category iterator_category;
typedef T value_type; //迭代器所指之物的数据类型(其余的见下面介绍)
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
};
//traits萃取机,其中I为迭代器类
template<class I>
struct iterator_traits {
typedef typename I::iterator_category iterator_category;
typedef typename I::value_type value_type;
typedef typename I::difference_type difference_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
};
- 当然,iteraotr_traits类还必须设计针对于迭代器是pointer以及pointer-to-const的特化版本(见后面一篇文章得迭代器介绍源码)
- 常用的迭代器相应类型有5中:value type、difference type、pointer、reference、iterator catagory
六、iterator源代码实例
- 下面列出了SGI STL<stl_iterator.h>头文件的代码
- 关于iostream iterators、inserter iterators、reverse iterators的实现,在后面配接器中介绍
迭代器类型
//用于标记迭代器的类型 struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag :public input_iterator_tag {}; struct bidirectional_iterator_tag :public forward_iterator_tag {}; struct random_access_iterator_tag :public bidirectional_iterator_tag {};
iterator
- 为了防止代码出错,自己定义的迭代器最好都继承这个类
template<class Category,class T, class Distance=ptrdiff_t,class Pointer=T*,class Reference=T&> struct iterator { typedef Category iterator_category; typedef T value_type; typedef Distance difference_type; typedef Pointer pointer; typedef Reference reference; };
traits萃取机
//萃取机traits template<class 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<class Iterator> struct iterator_traits<T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T& reference; }; //针对于pointer-to-const的偏特化版本 template<class Iterator> struct iterator_traits<const T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef const T* pointer; typedef const T& reference; };
- 下面这个函数可以方便的萃取出某个迭代器的类型(category)
//参数为迭代器 template<class Iterator> inline typename iterator_traits<Iterator>::iterator_category iterator_category(const Iterator&) { typedef typename iterator_traits<Iterator>::iterator_category category; return category(); }
- 下面这个函数可以方便的萃取出某个迭代器的distance type
//参数为迭代器 template<class Iterator> inline typename iterator_traits<Iterator>::difference_type* distance_type(const Iterator&) { return static_cast<typename iterator_traits<Iterator>::difference_type*>(0); }
- 下面这个函数可以方便的萃取出某个迭代器的value type
//参数为迭代器 template<class Iterator> inline typename iterator_traits<Iterator>::value_type* distance_type(const Iterator&) { return static_cast<typename iterator_traits<Iterator>::value_type*>(0); }