iterator模式定义如下:提供一种方法,使之能够依序寻访某个聚合物所含的各个元素,而又无需暴露该聚合物的内部表达形式
其中,c++里面各个容器的iterator扮演着将数据容器与算法结合起来的重要角色
将范型算法(find, count, find_if)用于某个容器中,最重要的是要给算法提供一个访问容器元素的工具,iterator
就扮演着这个重要的角色
我们在算法中可能会定义简单的中间变量或者设定算法的返回变量类型,这时候需要知道迭代器所指元素的类型是什么,但是由于没有typeof
这类判断类型的函数,我们无法直接获取,那该如何是好?
不要急,那首先先介绍一下iterator_tarit
iterator_trait
template
struct iterator_traits<_Tp*>
{
typedef ptrdiff_t difference_type;
typedef typename _Tp::value_type value_type;
typedef typename _Tp::pointer pointer;
typedef typename _Tp::reference reference;
typedef typename _Tp::iterator_category iterator_category;
};
看到这个奇奇怪怪的东西,是不是感觉没什么用,嗯,没关系,先记着
下面,将接着之前的话题,来看看如何提取出iterator
所指向的元素类型
value_type
例如
使用typedef
我们可以在迭代器中添加元素的类型
template
struct MyIter {
typedef T value_type;
T * ptr;
MyIter(T * p = 0) : ptr (p) {};
T& operator* () const { return *ptr;}
};
template
typename I::value_type //取出迭代器类中的类型
//用以设定返回变量类型,但是如果I是指针就会错误
get (I ite) {
return *ite;
}
但是,这个版本并不支持原生指针,然而就迭代器的行为而言,就是面向容器的指针,而正常的STL算法也是支持原生指针的,就如同下面的find一样
指针和迭代器的作用无非就是为stl算法提供了一个运算范围以及对容器(无论是vector,list,亦或是array)的访问
int main() {
int a[5] = {1,2,2,2,2};
int *begin = a;
int *end = a+5;
int count = std::count(begin, end, 2); //ok!
return 0;
}
所以对于第一个版本,我们还要对指针类型进行模版偏特化
提取以及偏特化
前面也提到了,如果直接使用typename I::value_type
,算法就无法接收原生指针,因为原生指针根本就没有value_type
这个内嵌类型
因此,我们还需要加入一个中间层对其进行判断,看它是不是原生指针,注意,这就是traits
技法的妙处所在
如果我们只使用上面的做法,也就是内嵌value_type
,那么对于没有value_type
的指针,我们只能对其进行偏特化,这种偏特化是针对可调用函数get
的偏特化,假如get
有100行代码,那么就会造成极大的视觉污染
#include
template
struct MyIter {
typedef T value_type;
T * ptr;
MyIter(T * p = 0) : ptr (p) {};
T& operator* () const { return *ptr;}
};
template
typename I::value_type //取出迭代器类中的类型
get (I ite) {
std::cout << "class version" << std::endl;
return *ite;
}
template
I get(I* ite) {
std::cout << "pointer version" << std::endl;
return *ite;
}
template
I get(const I* ite) {
std::cout << "const pointer version" << std::endl;
return *ite;
}
int main() {
int i = 3;
const int k = 3;
MyIter v(&i);
std::cout << get(v) << std::endl;
std::cout << get(&i) << std::endl;
std::cout << get(&k) << std::endl;
return 0;
}
就如同上面这个形式,设想往get中填充100行代码,简直不忍直视,你再看看下面这个,简直优雅!
利用一个中间层iterator_traits
固定了get
的形式,使得重复的代码大量减少,唯一要做的就是稍稍特化一下iterator_tartis
使其支持pointer
和const pointer
:)
#include
template
struct iterator_traits {
typedef typename T::value_type value_type;
};
template
struct iterator_traits {
typedef T value_type;
};
template
struct iterator_traits {
typedef T value_type;
};
template
struct MyIter {
typedef T value_type;
T * ptr;
MyIter(T * p = 0) : ptr (p) {};
T& operator* () const { return *ptr;}
};
template
typename iterator_traits::value_type
get (I ite) {
std::cout << "normal version" << std::endl;
return *ite;
}
int main() {
int i = 3;
const int k = 3;
MyIter v(&i);
std::cout << get(v) << std::endl;
std::cout << get(&i) << std::endl;
std::cout << get(&k) << std::endl;
return 0;
}
通过定义内嵌类型,我们获得了知晓iterator
所指元素类型的方法,通过traits
技法,我们将函数模板对于原生指针和自定义iterator
的定义都统一起来
这就是traits
技法的妙处所在
difference type
difference type
用于表示两个迭代器之间的距离的一个类型,也可以用来表示一个容器的最大的容量,因为对于连续空间的容器,头尾之间的距离就是最大容量
例如count()
就必须返回的类型就是迭代器的difference type
对于STL容器类型,以及原生指针,traits有如下两个不同版本
template
struct iterator_traits {
...
typedef typename I::difference_type difference_type;
}
//原生指针
template
struct iterator_traits {
...
typedef ptrdiff_t difference_type;
}
template
struct iterator_traits {
...
typedef ptrdiff_t difference_type;
}
reference type
标示了引用类型
pointer
标示了指针类型
测试
以上说明了迭代器内部的几种重要类型
下面对其进行一个测试,以此产生一个更直观的印象
#include
#include
#define Test(x,z,y) std::cout<::z,y>::value<::iterator
Test(IVec,value_type,int); //true
Test(IVec,difference_type,ptrdiff_t); //true
Test(IVec,reference,int&); //true
Test(IVec,pointer,int*); //true
return 0;
}
从上面可以看出,一个vector
-
value_type
=int
-
difference_type
=ptrdiff_t
-
reference
=int&
-
pointer
=int*
总结
要牢记iterator
是为了访问容器内的元素而存在的,而它内置的类型就是范型算法与容器进行沟通的重要工具
而我们使用traits
技法主要是为了解决原生指针和自定义iterator
之间的不同所造成的代码冗余
type traits
type traits
的出现和STL对于性能的要求有着千丝万缕的联系
试想,对于vector
这种大块分配内存,然后大块析构的容器,如果容器里面是POD的话,那么只要等它的生命周期结束就行了,如果是非POD的话,那么就要判断是否拥有no-traits
的析构函数
如果是这样的话,又回到了之前value_type
的窘境,因此,我们只需要使用type_traits
,对POD进行偏特化,通过两个神奇的类型进行判断
struct _true_type{};//无意义的析构函数
struct _false_type{};//有意义的析构函数
这样子就可以让负责析构的模块进行判断了
具体的type_traits
如下所示
template
struct type_traits
{
typedef _false_type has_trivial_default_constructor;//默认构造函数是否有意义?
typedef _false_type has_trivial_copy_constructor;//拷贝构造函数是否有意义?
typedef _false_type has_trivial_assgignment_constructor;//拷贝赋值操作是否有意义?
typedef _false_type has_trivial_destructor;//析构函数是否有意义?
/*POD意指Plain Old Data,也就是标量型别或传统的C struct(传统的C struct只能
包含数据成员,不能包含函数成员。也就是所谓的聚合类。POD型别必然包含无意义
的ctor/dtor/copy/assignment函数。
*/
typedef _false_type is_POD_type;//是否为Plain Old Data?
};
总结
通过对type_traits
进行特化,标注自己类中的构造,拷贝等行为是否是有意义的,可以大大提高适配算法的效率,这也是type traits
存在的意义