traits
前言
都说traits 是STL源码的钥匙,今天看来完了这一章节,确实强大,我先讲其实现的内部原理。在我看懂了traits之后,我就想,既然traits如此的强大,那我就以使用这的角度来领略它的美好,顺便加强它的理解,还是先浅谈traits的一些原理
为了取得iterator 的类型,在STL可花费了大量的精力:
迭代器的类型:
第一种获取迭代器的类型之法:利用模板函数参数推导机制:
看书中的例子:
//main函数中如此定义 int main() { int i; func(&i); //这里传的是地址 } //调用了func函数,我们来看看这个函数是怎样定义的 void func(I iter) { func_impl(iter,*iter); //哦,又调用了另外一个函数,有两个参数,第一个是原来的参数 I类型,第二个是 *I 的类型,也就是*(&i) = i,恢复 了原来的对象类型 }//好的再看看func_impl到底有何玄机 template<class I, class T> void fucn_impl(I iter, T t) { T tmp ;// 呵呵,T的类型那就是i的类型,那么在不知道i类型的情况下通过两巧妙的调用很好的利用了函数模板的参数推到机制 }
好的,很完美的推出了我们未知对象的类型,但是,如果我要的对象类型表示函数的返回值,那么这个方法就不可行了,因为我们是在间接调用了两个函数,而且最后在一个函数体的里边取得的类型
显然用于修饰函数的返回值不合适 ,这个办法不通用了,怎么办呢,书中的第二办法是声明内嵌类型
声明内嵌类型:
比如说:
</pre><pre name="code" class="cpp">template <class T> struct MyIter { typedef T value_type; //这句代码就可以通过value_type 来去的 T 这个类型了(如果对于这句代码的作用不太理解的话, //可以看看我之前的那篇关于typedef的实验博客 <a target=_blank href="http://blog.csdn.net/dalleny/article/details/39016201">http://blog.csdn.net/dalleny/article/details/39016201</a>, //调用的时候可以这样写 MyIter::value_type ,那么在不知道T 的情况下 //就可以取得T的类型 修饰定义变量就可以 MyIter::value_type a; // 修饰函数返回值就有typename MyIter::value_type fun(){return XX;} , //typename 是关键字,表示后面的MyIter::value_type 是一个类型,而不是成员变量,只有类型才能修饰返回值 T *ptr ; //指针类型 MyIter(T * p = 0):ptr(p){}//构造函数,初始化变量 ptr T & operator*() const{return *ptr;} //重载运算符 *,达到解引用的目的 //其他工作 }
貌似挺完美了,其实不然,因为我们的一切操作都是建立在迭代器的类型都是class type (都是类的形式),如果迭代器不是class type 比如说 原生指针,class 都没有了,何来 member ,那么typedef T value_type 这句话就无处可写了
为了使得上面的做法变得通用可行,所有指针均可适用,特偏化(特化)可以做到,(特偏化的最简单解释就是对template的参数进行更进一步的限制)
template <typename T> class C{ }; //特化后,限制多一点,有点像容器vector<int> template <typename T> class C<T*> { } //T可以是任何类型
这样的话,即使原生指针不是class 类型,也不怕
通过作为一个模板的参数 传进去,就可以实现声明内嵌类型
榨汁机出场啦
template<typename T> struct iterator_traits { typedef typename I::value_type value_type //通过iterator_traits ,可以轻松榨出T 迭代器的类型 //(这里可以看出每个迭代器必须有一个value_type,俨然已成为标准 //你不这样写,就不兼容,进不了stl大家庭) };
template<typename T> struct iterator_traits<I> { typedef typename I::value_type value_type ; }
所有的迭代器均可通过iterator_traits取得相应的类型
恩,除了value_type ,迭代器通常还有其他的特性可供iterator榨取,
五种迭代器的内部类型定义
1.value type 迭代器所指向的对象的类型 比如说 vector<int >::iterator it; 那么 value type 的值为 int
2.difference type 可以表示两个迭代器之间的距离 ,比如说,可以用这种类型来计数, 如,typename iterator_traits<I>::difference_type n =0, for(I first ; first != end; ++first ) n++; //n这种变量可做计数,算容量
3.reference type 引用类型,
4.pointer type 指针类型
5.iterator _category 迭代器本身的类型 (详情看下一小节),这个内部类型可以用于标识本身的类型,可用函数重载时候的版本识别, 所有重载时候的版本识别:主要是由于迭代器有5大类,无疑random_access_iterator 的功能是最强大的,但是并不是每次都是用最强大的一个版本性能就是最高的,由于其功能强大了,无疑内部实现会复杂一点,既有可能性能会有所下降,不过相当多的迭代器都是Random Access iterator 这种类型的,如果一个算法find()能够接受一个input_iterator,那么给它Random
Access iterator 未必最好,因此,可以实现接受五个版本 的迭代器类型 find()函数,按需选择,(注意不要使用动态绑定,这样会大大减低性能),可以在算法中find(Iterator , iterator tag),加入一个用于识别的参数,然后通过,iterator_traits<Iterator >::category萃取出来这个category类型,可以实现函数版本识别
上面说的5中是迭代器内部预定义的内部类型,可以方便我们使用,最后一个类型表示的是迭代器本身的类型:好!下面说它
五种迭代器的类型
个人小例子测试
#include<iostream> #include<vector> #include<list> #include<deque> #include<iterator> using namespace std; //下面定义5个版本的func函数,为了简单起见,就一个参数,分别表示不同的迭代器类型 void func(random_access_iterator_tag) { cout<<"random_access_terator 类型的迭代器"<<endl; } void func(bidirectional_iterator_tag) { cout<<"bidirectional_terator 类型的迭代器"<<endl; } void func(forward_iterator_tag) { cout<<"forward_terator 类型的迭代器"<<endl; } void func(input_iterator_tag) { cout<<"input_terator 类型的迭代器"<<endl; } void func(output_iterator_tag) { cout<<"output_terator 类型的迭代器"<<endl; } //这里提供一个接口给用户使用fun() template<class InputIterator > void fun(InputIterator it) {/这句代码最关键,利用iterator_traits<IputIterator>萃取出 iterator_category属性 typedef iterator_traits<InputIterator>::iterator_category category; func(category()); //调用去萃取 } int main() { vector<int> ve ; for(int i = 0;i<10;i++) ve.push_back(i); int cc=5; int *d = &cc; //第一个迭代器,指向int 类型 //第二个迭代器 vector<int>::iterator a = ve.begin(); //第三个迭代器 std::istream_iterator<int> is ; /分别对其调用fun函数 fun(d); fun(a); fun(is); system("pause"); return 0; }
结果如下
经测试,traits 发挥功效
SGI STL私房菜 _type_traits
这是STL神器,通过 iterator_traits这个强大的武器,我们可以榨取出iterator的5种特性,还帮助我们定义相应的类型
而_type_traits 这个神器一出,那就牛逼了,可以判断几乎所有的类型的特性,判断它们的五种特性
1.是否含有 trivial_default_constructor
2.是否含有 trivial_copy_constructor
3.是否含有 trivial_assignment_operator
4.是否含有trivial_destructor
5.是否为POD类型
trivial 是没有价值的意思,如果前4个函数返回 _true_traits (相应的有_false_traits)证明它们的那些构造析构函数,或者拷贝运算符都是无价值的,没有必要调用,直接可以使用简单的memcopy()函数 或者memmove()函数,这样效率大大提高,STL的哲学就是高性能,高效率!!!
第5个所谓的POD 就是plain old data,旧风格数据,也就是C风格的数据,可以理解为只有成员变量没有函数和指针的struct,因此,直接可以进行成员的复制操作,无需调用什么构造函数 (我做一个大胆的断定,前面四个都返回_true_traits,那么第5个必定返回_true_traits)