条款47 请使用traits classes表现类型信息
请使用traits classes表现类型信息
这一条款主要来讨论模板中迭代器的属性iterator_category,它可以通过类似于vector::iterator::iterator_category的方式来取得。
到这里我们有必要学习一下STL迭代器的类型,总共有五种,分别是:
input_iterator:只读,只能逐个前移
output_iterator:只写,只能逐个前移
forward_iterator:可读可写,只能逐个前移
bidirectional_iterator:可读可写,支持逐个前移和后移
random_access_iterator:可读可写,支持随机访问(任意步数移动)
为了表明容器内使用的是哪一种迭代器,STL在定义迭代器会总会打个一个标记“tag”,每个tag都是一个空的结构体,对应于以上五种迭代器,tag也有五个:
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag: public input_iterator_tag{};
bidirectional_iterator: public forward_iterator_tag{};
random_access_iterator: public bidirectional_iterator_tag{};
注意这五个tag之间,有些存在继承关系。
这个标记有什么用呢?STL在写vector的时候,会这样:
template class <T>
class vector
{
public:
class iterator
{
public:
typedef random_access_iterator iterator_category;
}
}
写list的时候,会这样写:
template class <T>
class list
{
public:
class iterator
{
public:
typedef bidirectional_iterator iterator_category;
…
}
…
}
既然迭代器已经由tag说明了它的类型(双向的,还是随机访问),那我们如何去利用它呢?比如现在我想要写一个迭代器前移的通用函数DoAdvance,不同迭代器类型会有不同的实现方式,所以我们可以像下面这样:
template <class T>
void DoAdvance(T Container)
{
typedef T::iterator::iterator_category IteratorCategory;
if (typeid(IteratorCategory) == typeid(input_iterator_tag))
{
cout << "Do manner in input_iterator_tag" << endl;
}
else if (typeid(IteratorCategory) == typeid(output_iterator_tag))
{
cout << "Do manner in output_iterator_tag" << endl;
}
else if (typeid(IteratorCategory) == typeid(forward_iterator_tag))
{
cout << "Do manner in forward_iterator_tag" << endl;
}
else if (typeid(IteratorCategory) == typeid(bidirectional_iterator_tag))
{
cout << "Do manner in bidirectional_iterator_tag" << endl;
}
else if (typeid(IteratorCategory) == typeid(random_access_iterator_tag))
{
cout << "Do manner in random_access_iterator_tag" << endl;
}
}
参数T是容器的类型,比如vector,如果像下面这样调用:
vector<int> v;
DoAdvance(v);
那么输出是Do manner in random_access_iterator_tag,因为vector的迭代器是随机访问型的,可以按随机访问类型的处理方式来去实现前移操作。typeid返回结果是名为type_info的标准库类型的对象的引用,它指明了这个对象/定义的类型。
用法实例
#include <vector>
#include <list>
#include <deque>
#include <iostream>
using namespace std;
template<typename T>
struct Iterator_traits
{
typedef typename T::value_type value_type;
typedef typename T::iterator_category iterator_category;
};
template<typename T>
struct Iterator_traits<T*>
{
typedef typename T::value_type value_type;
typedef random_access_iterator_tag iterator_category;
};
template<typename T>
struct Iterator_traits<const T*>
{
typedef typename T::value_type 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
system("pause");
return 0;
}
本例子取自:
Effective C++书上的源码
and:
http://blog.csdn.net/wuzhekai1985/article/details/6656659
这位作者的博客(STL中的C++技术)都很不错,值得推敲!
关于C++模板技术,以及在STL的运用的技术还有很多,后续会继续更新!