STL源码剖析--迭代器(转)
一、为什么需要traits编程技术
前面说了很多关于traits的光荣事迹,但是却一直没有介绍traits究竟是个什么东西,究竟是用来干什么的?traits在英文解释中就是特性,下面将会引入traits技术的作用,一步一步地揭开其神秘的面纱。
1.1 内嵌类型声明
1.1.1 以迭代器所指对象的类型声明局部变量
下面是一个以迭代器为模板形参的函数模板:
- template<typename Iterator>
- void func(Iterator iter)
- {
- //函数体
- }
template<typename Iterator>
void func(Iterator iter)
{
//函数体
}
假如现在算法中需要声明一个变量,而变量的类型是迭代器所指对象的类型,应该怎么处理呢?
- template<typename Iterator>
- void func(Iterator iter)
- {
- *Iterator var;//这样定义变量可以吗?
- }
template<typename Iterator>
void func(Iterator iter)
{
*Iterator var;//这样定义变量可以吗?
}
上面的代码是不可以通过编译的,虽然C++支持sizeof(),但是并不支持typeof(),就算是用到RTTI性质中的typeid(),获取到的也仅仅是类型的名字,因此不能直接用来声明变量。此时可以利用函数模板的参数类型推导机制解决问题,例如:
- template<typename Iterator, typename T>
- void func_impl(Iterator iter, T t)
- {
- T temp;//这里就解决了问题
- //这里做原本func()的工作
- }
- template<typename Iterator>
- void func(Iterator iter)
- {
- func_impl(iter, *iter);//func的工作全部都移到func_impl里面了
- }
- int main(int argc, const char *argv[])
- {
- int i;
- func(&i);
- }
template<typename Iterator, typename T>
void func_impl(Iterator iter, T t)
{
T temp;//这里就解决了问题
//这里做原本func()的工作
} template<typename Iterator>
void func(Iterator iter)
{
func_impl(iter, *iter);//func的工作全部都移到func_impl里面了
} int main(int argc, const char *argv[])
{
int i;
func(&i);
}
函数func作为对外接口,实际的操作却由函数func_impl执行,通过函数func_impl的参数类型推导,获取到Iterator指向对象的类型T,从而解决了问题。
1.1.2 以迭代器所指对象的类型声明返回类型
现在通过函数模板的参数类型推导解决了函数体内声明变量的问题,但问题又来了,如果需要返回类型是迭代器所指对象的类型又可以怎样做呢?
- template<typename Iterator>
- (*Iterator) func(Iterator iter)
- {
- //这样定义返回类型可以吗?
- }
template<typename Iterator>
(*Iterator) func(Iterator iter)
{
//这样定义返回类型可以吗?
}
在这种情况下,模板的参数类型推导机制也无能为力了,因为它只能推导参数,并不能推导函数的返回类型。STL解决这种问题的办法就是内嵌类型声明,即在迭代器内部添加一种“特性”,通过这种“特性”,算法可以很容易地获知迭代器所指对象的类型,请看下面的代码:
- template<typename T>
- class Iterator
- {
- public:
- typedef T value_type;//内嵌类型声明
- Iterator(T *p = 0) : m_ptr(p) {}
- T& operator*() const { return *m_ptr;}
- //...
- private:
- T *m_ptr;
- };
- template<typename Iterator>
- typename Iterator::value_type //以迭代器所指对象的类型作为返回类型,长度有点吓人!!!
- func(Iterator iter)
- {
- return *iter;
- }
- int main(int argc, const char *argv[])
- {
- Iterator<int> iter(new int(10));
- cout<<func(iter)<<endl; //输出:10
- }
template<typename T>
class Iterator
{
public:
typedef T value_type;//内嵌类型声明
Iterator(T *p = 0) : m_ptr(p) {}
T& operator*() const { return *m_ptr;}
//... private:
T *m_ptr;
}; template<typename Iterator>
typename Iterator::value_type //以迭代器所指对象的类型作为返回类型,长度有点吓人!!!
func(Iterator iter)
{
return *iter;
} int main(int argc, const char *argv[])
{
Iterator<int> iter(new int(10));
cout<<func(iter)<<endl; //输出:10
}
函数func()的返回类型前面必须加上关键词typename,原因在本人之前写的“C++模板学习”中也解释过,因为T是一个template参数,编译器在编译实例化func之前,对T一无所知,就是说,编译器并不知道Iterator<T>::value_type是一个类型,或者是一个静态成员函数,还是一个静态数据成员,关键词typename的作用在于告诉编译器这是一个类型,这样才能顺利通过编译。
1.2 原生指针也是一种迭代器
之前在介绍迭代器的分类之时说过,原生指针也是一种迭代器,此时问题就来了,原生指针并不是一种类类型,它是无法定义内嵌类型的。因此,上面的内嵌类型实现还不能完全解决问题,那可不可以针对原生指针做特殊化的处理呢?答案是肯定的,利用模板偏特化就可以做到了。
《泛型思维》一书对模板偏特化的定义是:
针对template参数更进一步的条件限制所设计出来的一个特化版本。
- //这个泛型版本允许T为任何类型
- template<typename T>
- class C
- {
- //...
- };
//这个泛型版本允许T为任何类型
template<typename T>
class C
{
//...
};
我们很容易接受上面的类模板有一个形式如下的偏特化版本:
- template<typename T>
- class C<T*>
- {
- //...
- };
template<typename T>
class C<T*>
{
//...
};
这个特化版本仅适用于T为原生指针的情况,”T为原生指针”就是“T为任何类型”的一个更进一步的条件限制。那如何利用模板偏特化解决原生指针不能内嵌类型的问题呢?下面介绍的iterator_traits就是关键了。
二、迭代器萃取机--iterator_traits
2.1 原生指针并不是一种类类型
STL里面使用iterator_traits这个结构来专门“萃取”迭代器的特性,前面代码中提到的value_type就是迭代器的特性之一:
- template<typename Iterator>
- struct iterator_traits
- {
- typedef typename Iterator::value_type value_type;
- };
template<typename Iterator>
struct iterator_traits
{
typedef typename Iterator::value_type value_type;
};
如果Iterator有定义value_type,那么通过iterator_traits作用之后,得到的value_type就是Iterator::value_type,比较之前写的版本和经iterator_traits作用后的版本:
- template<typename Iterator>
- typename Iterator::value_type //这行是返回类型
- func(Iterator iter)
- {
- return *iter;
- }
- //通过iterator_traits作用后的版本
- template<typename Iterator>
- typename iterator_traits<Iterator>::value_type //这行是返回类型
- func(Iterator iter)
- {
- return *iter;
- }
template<typename Iterator>
typename Iterator::value_type //这行是返回类型
func(Iterator iter)
{
return *iter;
} //通过iterator_traits作用后的版本
template<typename Iterator>
typename iterator_traits<Iterator>::value_type //这行是返回类型
func(Iterator iter)
{
return *iter;
}
从长度上看,好像需要敲的代码更多了,为什么要这么麻烦加上一层间接层呢?由于原生指针也是一种迭代器,而且不是一种类类型,因此原生指针并不能定义内嵌类型。这里通过实现iterator_traits的一个偏特化版本就可以解决这个问题了,具体的实现如下:
- //iterator_traits的偏特化版本,针对迭代器是个原生指针的情况
- template<typename T>
- struct iterator_traits<T*>
- {
- typedef T value_type;
- };
//iterator_traits的偏特化版本,针对迭代器是个原生指针的情况
template<typename T>
struct iterator_traits<T*>
{
typedef T value_type;
};
大家在进行函数重载的时候,应该都曾遇到过以下的情况:
- //函数版本一
- void func(int *ptr)
- {
- //...
- }
- //函数版本二
- void func(const int *ptr)
- {
- //...
- }
//函数版本一
void func(int *ptr)
{
//...
} //函数版本二
void func(const int *ptr)
{
//...
}
以上两个函数虽然函数、形参个数和位置都一样,但它们不是同一个函数,而是函数重载的一种情况,也就是说函数形参的const和非const版本是不一样的,在函数版本一里面,可以修改指针ptr指向的数据,但是在函数版本二里面却不可以,因为传入的指针ptr是一个const指针。由此可以联想到,当将一个const指针作为模板形参传给前面声明的偏特化版本的iterator_traits会有发生什么情况呢?
- iterator_traits<const int*>::value_type //获得的value_type是const int,并不是int
iterator_traits<const int*>::value_type //获得的value_type是const int,并不是int
当我们想用iterator_traits萃取出value_type并声明一个临时变量时,却发现声明的变量是const类型,并不能进行赋值,这违背了我们的用意。我们需要一种方法区别const和非const才能避免这种误会的发生,答案很简单,只要另外再设计一个iterator_traits偏特化版本就可以了:
- template<typename T>
- struct iterator_traits<const T*>
- {
- typedef T value_type;
- }
template<typename T>
struct iterator_traits<const T*>
{
typedef T value_type;
}
现在,不论是自定义的迭代器,还是原生指针int*或者是const int*,都可以通过iterator_traits获取到正确的value_type。
2.2 iterator_traits中定义的类型
STL根据经验,定义了迭代器最常用到的五种类型:value_type、difference_type、pointer、reference、iterator_category,任何开发者如果想将自己开发的容器与STL结合在一起,就一定要为自己开发的容器的迭代器定义这五种类型,这样都可以通过统一接口iterator_traits萃取出相应的类型,下面列出STL中iterator_traits的完整定义:
- tempalte<typename I>
- struct iterator_traits
- {
- typedef typename I::iterator_category iterator_category;
- typedef typename I::value_type value_type;
- typedef typeanme I:difference_type difference_type;
- typedef typename I::pointer pointer;
- typedef typename I::reference reference;
- };
tempalte<typename I>
struct iterator_traits
{
typedef typename I::iterator_category iterator_category;
typedef typename I::value_type value_type;
typedef typeanme I:difference_type difference_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
};
下面会分别介绍一下这五种类型:
(1) 迭代器类型之一:value_type
value_type就是指迭代器所指对象的类型,例如,原生指针也是一种迭代器,对于原生指针int*,int即为指针所指对象的类型,也就是所谓的value_type。
(2) 迭代器类型之二:difference_type
difference_type用来表示两个迭代器之间的距离,例如:
- int array[5] = {1, 2, 3, 4, 5};
- int *ptr1 = array + 1;//指向2
- int *ptr2 = array + 3;//指向4
- ptrdiff_t distance = ptr2 - ptr1;//结果即为difference_type
int array[5] = {1, 2, 3, 4, 5};
int *ptr1 = array + 1;//指向2
int *ptr2 = array + 3;//指向4
ptrdiff_t distance = ptr2 - ptr1;//结果即为difference_type
上面代码中,指针ptr2与ptr1相减的结果的类型就是difference_type,对于原生指针,STL以C++内建的ptrdiff_t作为原生指针的difference_type。
(3) 迭代器类型之三:reference_type
reference_type是指迭代器所指对象的类型的引用,reference_type一般用在迭代器的*运算符重载上,如果value_type是T,那么对应的reference_type就是T&;如果value_type是const T,那么对应的reference_type就是const T&。
(4) 迭代器类型之四:pointer
pointer就是指迭代器所指的对象,也就是相应的指针,对于指针来说,最常用的功能就是operator*和operator->两个运算符。因此,迭代器需要对这两个运算符进行相应的重载工作:
- T& operator*() const { return *ptr; } // T& is reference type
- T* operator->() const { return ptr; } // T* is pointer type
T& operator*() const { return *ptr; } // T& is reference type
T* operator->() const { return ptr; } // T* is pointer type
(5) 迭代器类型之五:iterator_category
iterator_category的作用是标识迭代器的移动特性和可以对迭代器执行的操作,从iterator_category上,可将迭代器分为Input Iterator、Output Iterator、Forward Iterator、Bidirectional Iterator、Random Access Iterator五类,具体为什么要这样分类,简单来说,就是为了尽可能地提高效率,这也是STL的宗旨之一。具体的情况已经在本人的“《STL源码剖析》学习之迭代器”中详细介绍过,这里就不在多说了。
2.3 iterator_traits完整定义
为了保证iterator_traits可以正常工作,STL提供了一个iterator类,所有自定义的迭代器都必须继承自它,这样才能保证这些自定义的迭代器可以顺利地狱其它STL组件进行协作,iterator类具体定义如下:
- template<typename Category,
- typename T,
- typename Distance = ptrdiff_t,
- typename Pointer = T*,
- typename Reference = T&>
- struct iterator
- {
- typedef Category iterator_category;
- typedef T value_type;
- typedef Distance difference_type;
- typedef Pointer pointer;
- typedef Reference reference;
- };
template<typename Category,
typename T,
typename Distance = ptrdiff_t,
typename Pointer = T*,
typename Reference = T&>
struct iterator
{
typedef Category iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
};
类iterator不包含任何成员变量,只有类型的定义,因此不会增加额外的负担。由于后面三个类型都有默认值,在继承它的时候,只需要提供前两个参数就可以了,如:
- template <typename T>
- class ListIter : public std::iterator<std::forward_iterator_tag, T>
- {
- //...
- }
最新文章
- get和post的区别
- Android使用文件存储数据
- css弹性盒子学习
- Sqli-labs less 27
- 汉化Eclipse+配色方法(官方语言包)
- java classpath、path用法
- Educational Codeforces Round 8 D. Magic Numbers
- 删除WIN7系统的共享文件
- #python基础学习模块:marshal 对象的序列化
- double类型如何保留2为小数
- Pascal向C++的跨越
- Android - 直线(line)画法
- iframe子页面调用父页面javascript函数的方法
- 基础才是重中之重~关于ThreadStatic和Quartz的一点渊源
- 201421123042 《Java程序设计》第8周学习总结
- Dynamics 365 CE的插件/自定义工作流活动中调用Web API示例代码
- uboot - the bootloader of linux
- MemoryCache
- JS数字指定长度不足前补零的实现
- git 的安装和使用及hithub同步