这一条款主要来讨论模板中迭代器的属性iterator_category,它能够经过相似于vector<int>::iterator::iterator_category的方式来取得。dom
到这里咱们有必要学习一下STL迭代器的类型,总共有五种,分别是:函数
input_iterator:只读,只能逐个前移学习
output_iterator:只写,只能逐个前移测试
forward_iterator:可读可写,只能逐个前移spa
bidirectional_iterator:可读可写,支持逐个前移和后移指针
random_access_iterator:可读可写,支持随机访问(任意步数移动)code
为了代表容器内使用的是哪种迭代器,STL在定义迭代器会总会打个一个标记“tag”,每一个tag都是一个空的结构体,对应于以上五种迭代器,tag也有五个:对象
struct input_iterator_tag{};blog
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的时候,会这样:
1 template class <T> 2 class vector 3 { 4 public: 5 class iterator 6 { 7 public: 8 typedef random_access_iterator iterator_category; 9 … 10 } 11 … 12 } 13 14 写list的时候,会这样写: 15 template class <T> 16 class list 17 { 18 public: 19 class iterator 20 { 21 public: 22 typedef bidirectional_iterator iterator_category; 23 … 24 } 25 … 26 }
既然迭代器已经由tag说明了它的类型(双向的,仍是随机访问),那咱们如何去利用它呢?好比如今我想要写一个迭代器前移的通用函数DoAdvance,不一样迭代器类型会有不一样的实现方式,因此咱们能够像下面这样:
1 template <class T> 2 void DoAdvance(T Container) 3 { 4 typedef T::iterator::iterator_category IteratorCategory; 5 if (typeid(IteratorCategory) == typeid(input_iterator_tag)) 6 { 7 cout << "Do manner in input_iterator_tag" << endl; 8 } 9 else if (typeid(IteratorCategory) == typeid(output_iterator_tag)) 10 { 11 cout << "Do manner in output_iterator_tag" << endl; 12 } 13 else if (typeid(IteratorCategory) == typeid(forward_iterator_tag)) 14 { 15 cout << "Do manner in forward_iterator_tag" << endl; 16 } 17 else if (typeid(IteratorCategory) == typeid(bidirectional_iterator_tag)) 18 { 19 cout << "Do manner in bidirectional_iterator_tag" << endl; 20 } 21 else if (typeid(IteratorCategory) == typeid(random_access_iterator_tag)) 22 { 23 cout << "Do manner in random_access_iterator_tag" << endl; 24 } 25 }
参数T是容器的类型,好比vector<int>,若是像下面这样调用:
1 vector<int> v; 2 DoAdvance(v);
那么输出是Do manner in random_access_iterator_tag,由于vector<int>的迭代器是随机访问型的,能够按随机访问类型的处理方式来去实现前移操做。typeid返回结果是名为type_info的标准库类型的对象的引用,它指明了这个对象/定义的类型。
由于这里讨论的是迭代器,因此更常见的是直接传迭代器进去,像这样:
1 template <class IterT> 2 void DoAdvance(IterT Iter) 3 { 4 typedef IterT::iterator_category IteratorCategory; 5 if (typeid(IteratorCategory) == typeid(input_iterator_tag)) 6 { 7 cout << "Do manner in input_iterator_tag" << endl; 8 } 9 … 10 }
注意这里的模板参数是IterT,它表示一个迭代器的类型,好比vector<int>::iterator。这里是去主动访问iterator里面定义的属性iterator_category,咱们也能够经过trait classes来访问,像下面这样:
1 template <class IterT> 2 void DoAdvance(IterT Iter) 3 { 4 if (typeid(iterator_traits<IterT>::iterator_category) 5 == typeid(input_iterator_tag)) 6 { 7 cout << "Do manner in input_iterator_tag" << endl; 8 } 9 … 10 }
iterator_traits的定义以下:
1 template<class IterT> 2 struct iterator_traits<IterT> 3 { 4 typedef typename IterT::iterator_category iterator_category; 5 … 6 };
这个感受只是简化了输入代码量而已,本质上仍是去得到迭代器的tag,它有一个针对指针的偏特化版本,像下面这样:
1 template<class IterT> 2 struct iterator_traits<IterT*> 3 { 4 typedef random_access_iterator_tag iterator_category; 5 };
这里都是用typeid去进行类型判断的,它是在运行期才能执行,那么能不能放在编译期呢,固然能够,就是要用到函数的重载,像下面这样:
1 template <class IterT> 2 void DoAdvance(IterT Iter, input_iterator_tag) 3 { 4 cout << "Do manner in input_iterator_tag" << endl; 5 } 6 7 8 template <class IterT> 9 void DoAdvance(IterT Iter, random_access_iterator_tag) 10 { 11 cout << "Do manner in random_access_iterator_tag" << endl; 12 }
像下面这样使用;
1 vector<int>::iterator iter; 2 DoAdvance(iter, iterator_traits<vector<int>::iterator>::iterator_category());
注意迭代器的tag是能够直接做为函数形参的,这样就能够在编译期决定到底执行哪种迭代器的行为了。
条款标题的traint classes是一个广义的概念,咱们以前讨论的iterator_traits只是其一部分,除以以外,还有四份迭代器相关的信息(如value_type等),TR1导入许多新的trait classes,好比is_fundamental<T>等(判断T是不是内置类型)。
最后,咱们来总结一下:
1. Traits class使得类型相关信息能够在编译期可用,它们以template和template特化完成实现;
2. 整合重载技术后,traits classes有可能在编译期对类型执行if-else测试。