您的位置:首页 > 编程语言

STL学习笔记:Iterator和Traits编程技巧

2011-12-22 12:16 369 查看
References:

《STL源码剖析》

Overview

在STL中,在访问容器的元素的时候,一般我们会用Iterator来访问。使用Iterator访问,可以使得函数或者算法本身独立于容器本身的类型,也就是做到某种程度上的泛型。

而在STL中,原生指针本身也是一种iterator,那么对于泛型函数的设计就会带来复杂度,比如在定义一个针对iterator的swap函数的时候,他的定义可以如下:

template <class T> void swap ( T a, T b );
在定义的时候,有类似如下的定义:

template <class T> void swap ( T a, T b )
{
ValueTypeOfT c(*a); *a=*b; *b=c;
}
注意到这里的ValueTypeOfT表示T的迭代器所指的类型。

那么对于迭代器,我们应该怎么知道它的指的类型呢?对于原生指针呢?

ValueType of Iterator

对于迭代器,我们可以在定义一个迭代器的时候就把这个迭代器所指的类型给定义成ValueType,这样我们就可以在用的时候直接取出来。
如下定义Iterator:
template <typename T>
class Iterator
{
public:
typedef T ValueType;
private:
T* ptr;
};
然后我们在swap中就可以像下面那样去定义了:

template <class T> void swap ( T a, T b )
{
typename T::ValueType c(*a);
*a=*b;
*b=c;
}

但是要知道,原生指针也是一种迭代器。在上面的这段代码,对于原生指针是不可行的,因为原生指针是没有定义ValueType的!

那么我们要怎么去解决这个问题呢?

ValueType of Raw Pointer

我们知道,一个原生指针的所指类型在定义的时候就会有。比如T* a,那么a所指的类型就是T。所以T*的ValueType就是T。
那么我们要怎么让取出原生指针所指类型的过程和Iterator的过程统一起来呢?
在这里,STL引入了一个概念,叫做Trait。它指的是对于一个迭代器来说,它的相关的类型,比如说这个迭代器所指的类型,所指类型的引用,所指类型的指针等等。
对于一个迭代器,它应该定义好这些相关的类型。然后在使用它们的时候,就是用一个叫做IteratorTraits的Trait抽取器,来讲这个迭代器相关的类型抽取出来。
比如说对于普通的迭代器,我们将上面的定义修改一下,变成下面这样:
template <class T> void swap ( T a, T b )
{
typename IteratorTrait<T>::ValueType c(*a);
*a=*b;
*b=c;
}
而其中IteratorTrait的定义如下:
template <typename Iterator>
struct IteratorTrait
{
typedef Iterator::ValueType ValueType;
};


看起来好像有了这个IteratorTrait也还是没有解决我们的问题阿?对于原生指针我们还是取不出来我们想要的ValueType嘛!
莫急,STL这样做有它的原因。我们回想一下,对于一个模板类,我们可以定义类的偏特化。我们可以利用偏特化来达成我们的目的!
OK,我们的目的是对于原生指针,我们也要能够统一的找到它的ValueType。那么可以有如下的一个IteratorTrait的偏特化:
template <typename T>
struct IteratorTrait<T*>
{
typedef T ValueType;
};
有了这个偏特化之后,对于原生指针也可以用上面统一的swap的定义了!

所以这个IteratorTrait是个很神奇的东西,它帮助我们从Iterator里面去抽取出对于Iterator里面相关的东西。
其实在动态语言里面,对于元编程(metaprogramming)支持好的已经可以用一些动态的方法去抽取类型相关的东西。但是对于静态语言并且不支持反射的C++来说,模板的编译期自动检测类型就成为了在C++中实现元编程的一个非常重要的特性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: