STL学习之迭代器和trait编程技巧
一、迭代器的概念
迭代器是STL将数据容器和算法分开后连接的纽带,也是泛型思维发展的必然结果。泛型算法就是通过迭代器操作容器的,使得算法和容器本身分离开来。
迭代器模式:提供一种方式,可以依次访问一个聚合物(容器)中所有元素而不暴露聚合物内部的表达方式。
迭代器类似与智能指针,但是它一般不会对所指向的元素进行释放空间,因为迭代器只是在指针外面包裹一层外加一些操作。迭代器最重要编码工作是完成一些操作符的重载,这些重载都是针对指针类型的操作,例如,++,——,*,->等,不同类型的迭代器完成的功能都不相同,详解见下文。
迭代器定义的位置最好是在容器内,将定义的任务交给了容器的设计者,因为每一种容器都对应一种迭代器,而定义在内部也不会暴露容器的内部元素。
二、迭代器型别和trait编程
迭代器的型别主要有五种:value_type,catalog,reference,pointer,diffrence。分别代表这迭代器所指对象类型,迭代器类型,迭代器所指类型引用,迭代器所指类型指针,用什么类型表示迭代器之间距离(如int类型)。如何提取这些迭代器都有的特性呢?
首先,为所有的迭代器提供一个型别类型,每定义一个迭代器都必须继承该型别类型。
- 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;
- };
当定义迭代器的时候,必须给定迭代器的特性。STL为提取迭代器的特性,提供了一个模板类iterator_trait,适用于所有的迭代器和原生指针,定义如下
- template <class Iterator>
- struct iterator_traits
- {
- // 迭代器类型, STL提供五种迭代器
- typedef typename Iterator::iterator_category iterator_category;
- // 迭代器所指对象的型别
- // 如果想与STL算法兼容, 那么在类内需要提供value_type定义
- 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 T>
- 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;
- };
- // 针对指向常对象的指针提供特化
- template <class T>
- 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;
- };
指针并非类型,因此需要偏特化成一个模板对应指针的特性,可以看出,指针是随机访问迭代器类型。
迭代器的类型有五种:输入、输出、前向、双向、随机访问五种迭代器,输入和输出分别只读和只写,只能向前不能向后,前向迭代器可以进行读写,只能向前,双向迭代器可以向前和向后移动,但每次只能移动一次,随机访问迭代器可以跳跃移动,与原生指针操作相同。
STL中构建了这五种类别,用于标识迭代器的类别。
- // 用于标记迭代器类型
- 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 {};
可以看出继承关系,使用template元编程技术,之所以使用结构体或类型,是为了进行参数推导,将判断在编译期执行而非运行期,因为每个迭代器操作不同,因此需要不同的函数版本对应不同迭代器。
三、_type_trait
以上讲的是迭代器的特性提取,还有类型的特性提取。类型的型别主要有五种:has_trivial_default_constructor、has_trivial_copy_constructor、has_trivial_assignment_operator、has_trivial_destructor、is_POD_type。
STL提供的模板_type_trait类
- template <class type>
- struct __type_traits
- {
- // 不要移除这个成员
- // 它通知能自动特化__type_traits的编译器, 现在这个__type_traits template是特化的
- // 这是为了确保万一编译器使用了__type_traits而与此处无任何关联的模板时
- // 一切也能顺利运作
- typedef __true_type this_dummy_member_must_be_first;
- // 以下条款应当被遵守, 因为编译器有可能自动生成类型的特化版本
- // - 你可以重新安排的成员次序
- // - 你可以移除你想移除的成员
- // - 一定不可以修改下列成员名称, 却没有修改编译器中的相应名称
- // - 新加入的成员被当作一般成员, 除非编译器提供特殊支持
- typedef __false_type has_trivial_default_constructor;
- typedef __false_type has_trivial_copy_constructor;
- typedef __false_type has_trivial_assignment_operator;
- typedef __false_type has_trivial_destructor;
- typedef __false_type is_POD_type;
- };
_true_type和_false_type是结构体,用于标记真假,也是为了用于参数推导才使用类型的。STL对每个内置类型均进行了特化,且将所有型别标记为_true_type
指针不是类型,但是有此五种特性,进行偏特化
- template <class T>
- struct __type_traits<T*>
- {
- typedef __true_type has_trivial_default_constructor;
- typedef __true_type has_trivial_copy_constructor;
- typedef __true_type has_trivial_assignment_operator;
- typedef __true_type has_trivial_destructor;
- typedef __true_type is_POD_type;
- };
对于某些类型的指针可能有不同的型别,可以进行特化。
每种新定义的类型,都需要进行特化标识自己的特性,否则按照默认的全部为_false_type。