STL是C++标准库的重要组成部分之一,它不仅是一个可复用的组件库,更是一个包含算法与数据结构的软件框架,同时也是C++泛型编程的很好例子。STL中运用了许多C++的高级技术。本文介绍重载函数。主要参考了《C++ Primer》和《STL源码剖析》。
有人会问,STL大量运用了模板,哪里用过重载函数呢?首先介绍重载函数的概念。出现在相同作用域中的两个函数,如果具有相同的名字而形参表不同,则称为重载函数(overloaded function)。这是《C++ Primer》上对于重载函数的定义。在STL中,提到重载函数,必须先介绍一下迭代器。两者貌似扯不上什么关系,慢慢道来。我们知道STL的迭代器有五个特性,分别是value_type、pointer、 reference、 difference_type以及iterator_category。其中value_type已在前面两篇文章中介绍,表示迭代器所指的数据类型。本文来介绍一下iterator_category。
iterator_category表示迭代器的分类,共有五类。input_iterator、output_iterator、forward_iterator、bidirectional_iterator、random_access_iterator,分别是只读、只写、前向移动读写、双向移动读写、随机读写。任何一种迭代器,都属于其中的一种分类。比如单链表的迭代器属于forward_iterator,链表的迭代器属于 bidirectional_iterator,而双端队列的迭代器属于random_access_iterator。下面给出了代码示例,整理了一下。
- template <class T, class Ref, class Ptr>
- struct Slist_iterator //单链表
- {
- typedef T value_type; //迭代器所指数据类型
- typedef ptrdiff_t difference_type; //两个迭代器间的距离
- typedef Ptr pointer; //迭代器所指的数据,不允许改变,即可以当右值
- typedef Ref reference; //迭代器所指的数据,允许改变,即可以当左值
- typedef forward_iterator_tag iterator_category; //前向移动
- ...
- };
- template<class T, class Ref, class Ptr>
- struct List_iterator //双向链表
- {
- typedef ptrdiff_t difference_type;
- typedef T value_type;
- typedef Ptr pointer;
- typedef Ref reference;
- typedef bidirectional_iterator_tag iterator_category; //双向
- ...
- };
- template <class T, class Ref, class Ptr>
- struct Deque_iterator //双端队列
- {
- typedef T value_type;
- typedef ptrdiff_t difference_type;
- typedef Ptr pointer;
- typedef Ref reference;
- typedef random_access_iterator_tag iterator_category; //随机
- ...
- };
说了这么多,与重载函数有什么关系呢?再给出一个函数就清楚了,这个函数就是advance函数,它用来移动迭代器。下面是该函数的定义,摘自HP的STL源码,做了修改。不难看出,外部的接口是advance函数,内部用了三个重载函数,这些函数的第三形参非常有意思,只是一个类型,没有变量名。这个形参仅仅s是用来激活重载,很神奇吧,强大的C++啊。
这里进行函数的重载,应该是考虑到效率。对于双端队列,它的迭代器支持随机读写,那么调用第三个函数显然比调用第一个函数有效率。显然,这种效率的提升,与迭代器的移动能力息息相关。STL中的部分函数采用了这种策略,比如distance函数,为的是提高效率。
- template <class InputIterator, class Distance>
- inline void advance(InputIterator& i, Distance n)
- {
- _advance(i, n, iterator_category(i));
- }
- template <class InputIterator, class Distance>
- inline void _advance(InputIterator& i, Distance n, input_iterator_tag) //第一个函数
- {
- while (n--) ++i;
- }
- template <class BidirectionalIterator, class Distance>
- inline void _advance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag) //第二个函数
- {
- if (n >= 0)
- while (n--) ++i;
- else
- while (n++) --i;
- }
- template <class RandomAccessIterator, class Distance>
- inline void _advance(RandomAccessIterator& i, Distance n, random_access_iterator_tag) //第三个函数
- {
- i += n;
- }
到此,STL中重载函数的运用介绍的差不多了,不过有两个疑问。(1)_advance函数提供了三个重载版本,为什么没有定义inline void _advance (InputIterator& i, Distance n, forward_iterator_tag) 这个函数呢?(2)如何获取迭代器的类型呢?即上面的iterator_category(i)调用如何实现的。
对于第一个疑问,我们知道单链表的迭代器类型是forward_iterator,_advance貌似没有可选的函数以供调用。其实这与STL中迭代器的分类有关。下面是迭代器分类的关系表示,通过为迭代器设计标签类实现。_advance函数正是通过这些标签类激活重载的。代码如下:
- //迭代器各类间的关系
- 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 {};
从上面定义的关系,我们发现forward_iterator_tag继承自Input_iterator_tag,因此对于forward_iterator类的迭代器,其标签为forward_iterator_tag,通过向上的转换,就可以调用标签为Input_iterator_tag的那个重载版本。可以写个程序来验证一下。
- #include <iostream>
- using namespace std;
- //迭代器分类,首字符大写,与标准库区分一下
- 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 <class I>
- void foo(I& i, Input_iterator_tag)
- {
- cout<<"Input_iterator_tag version"<<endl;
- }
- template <class I>
- void foo(I& i, Bidirectional_iterator_tag)
- {
- cout<<"Bidirectional_iterator_tag version"<<endl;
- }
- template <class I>
- void foo(I& i, Random_access_iterator_tag)
- {
- cout<<"Random_access_iterator_tag version"<<endl;
- }
- int main()
- {
- int *x;
- foo(x, Input_iterator_tag()); //输出为Input_iterator_tag version
- foo(x, Forward_iterator_tag()); //输出为Input_iterator_tag version
- foo(x, Bidirectional_iterator_tag()); //输出为Bidirectional_iterator_tag version
- foo(x, Random_access_iterator_tag()); //输出为Random_access_iterator_tag version
- return 0;
- }
对于第二个疑问,好像遇到过,是在介绍模板特化中提到的。用了内嵌型别、模板特化的技术。STL还是运用这些技术解决这个问题的。扩展之前的萃取剂就可以了。下面给出了萃取剂新的定义,同时给出了测试用例,已在VS2008下测试通过。
至于内嵌型别及模板特化的具体介绍,详见STL运用的C++技术(2)——模板特化
- #include <vector>
- #include <list>
- #include <deque>
- #include <iostream>
- using namespace std;
- //萃取剂
- template<class I>
- struct Iterator_traits{
- typedef typename I::value_type value_type;
- typedef typename I::iterator_category iterator_category; //迭代器的类型
- };
- //特化 原生指针
- template<class T>
- struct Iterator_traits<T*>{
- typedef T value_type;
- typedef random_access_iterator_tag iterator_category;
- };
- //特化 原生常指针
- template<class T>
- struct Iterator_traits<const T*>{
- typedef T value_type;
- typedef random_access_iterator_tag iterator_category;
- };
- #define VALUE_TYPE(I) Iterator_traits<I>::value_type()
- #define ITERATOR_CATEGORY(I) Iterator_traits<I>::iterator_category()
- //自定义的advance函数,与STL差不多
- template <class InputIterator, class Distance>
- inline void MyAdvance(InputIterator &i, Distance n)
- {
- _MyAdvance(i, n, ITERATOR_CATEGORY(InputIterator)); //萃取迭代器的类型
- }
- template <class InputIterator, class Distance>
- inline void _MyAdvance(InputIterator& i, Distance n, input_iterator_tag)
- {
- while (n--) ++i;
- cout<<"InputIterator"<<endl;
- }
- template <class BidirectionalIterator, class Distance>
- inline void _MyAdvance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag)
- {
- if (n >= 0)
- while (n--) ++i;
- else
- while (n++) --i;
- cout<<"BidirectionalIterator"<<endl;
- }
- template <class RandomAccessIterator, class Distance>
- inline void _MyAdvance(RandomAccessIterator& i, Distance n, random_access_iterator_tag)
- {
- i += n;
- cout<<"RandomAccessIterator"<<endl;
- }
- //测试程序
- int main()
- {
- vector<int> v;
- v.push_back(1);
- v.push_back(2);
- list<int> l;
- l.push_back(1);
- l.push_back(2);
- deque<int> d;
- d.push_back(1);
- d.push_back(2);
- vector<int>::iterator iter1 = v.begin();
- list<int>::iterator iter2 = l.begin();
- deque<int>::iterator iter3 = d.begin();
- MyAdvance(iter1, 1); //vector的迭代器是原生指针,因此是RandomAccessIterator
- MyAdvance(iter2, 1); //链表的迭代器是双向的,因此是BidirectionalIterator
- MyAdvance(iter3, 1); //双端队列支持随机读写,因此是RandomAccessIterator
- return 0;
- }