__iterator_traits
技法用于模板编程,STL所有算法都是基于模板实现的。
特殊的特化:特化为指针 或 引用
STL中的例子: 特化为指针 和 底层指针:
//这段代码是STL源码中的一部分
template <class Iterator>
struct iterator_traits {
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
};
/*
* 偏特化版本,我们可以看到他的唯一不同之处就是T*,这也是偏特化的一种
*/
template <class T>
struct iterator_traits<T*> {
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};
例子2:特化为指针
#include <iostream>
#include <vector>
using namespace std;
//第一种方式
template <class Iter>
struct iter_traits//简化版的iterator_traits,只定义了value_type;为了区别于STL中的
//iterator::traits,名字也改为iter_traits
{
typedef typename Iter::value_type value_type;
};
//第二种方式,偏特化 为 指针类型
template <class Type>
struct iter_traits<Type *>
{
typedef Type value_type;
};
// algorithm
template <class Iter>
void iter_print(const Iter &a, const Iter &b)
{
typedef typename iter_traits<Iter>::value_type T;
//此处是关键,请思考一下:如果a,b是指针的话,除了用iter_traits的第二种方式,还有什么办法
//可以获得a或b所指向的数据的类型?
T key;
Iter p;
for(p=a;p!=b;p++)
{
key = *p;
cout << key << ",";
}
cout <<endl;
}
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,10};
vector<string> b{"A","B","C","D","E","F","G"};
iter_print<int *>(a,a+10);
iter_print<vector<string>::iterator>(b.begin(), b.end());
return 0;
}
运行结果:
1,2,3,4,5,6,7,8,9,10,
A,B,C,D,E,F,G,
令人费解的语法:
为什么使用typedef 后面 加 typename关键字?
typedef typename Iter::value_type value_type;
实际上,模板类型在实例化之前,编译器并不知道Iter::value_type
是什么东西,事实上一共有三种可能:
- 静态数据成员
- 静态成员函数
- 嵌套类型
那么此时typename的作用就在此时体现出来了——定义就不再模棱两可。
根据上述分析,
typedef typename Iter::value_type value_type;
语句的真实面目是:
typedef创建了存在类型的别名,而typename告诉编译器Iter::value_type
是一个类型而不是一个成员。
后续
在main里调用iter_print函数来打印一个数组和一个vector<string>
的内容。
iter_print是一个模板函数,有两个参数,其类型都是Iter,分别表示区间的开始和结束。(实际上在STL中,类似iter_print的函数有很多,比如sort,copy等等,它们都是模板函数,并且以iterator为参数)。
在iter_print中有一个需求,就是通过Iter来获取它所代表的容器中数据的类型,从而可以定义一个变量key(在这个函数中,key不是必须的,只是以它为例子,说明有这么一个需要):
T key;
一般的Iterator类中都会定义一个value_type
类型(比如vector<string>::iterator
这个类中会定义一个value_type
,其类型是string
),因此,在iter_traits的第一种定义方式中只要通过
typedef typename Iter::value_type value_type
就可以得到其类型。
但是,iter_print函数的参数还有可能是指针(请回想一下,STL中的sort函数就可以接受两个指针作为参数)。这样一来,第一种定义方式对指针来说就失效了(因为原生指针不是一种类型)。这时,为了能从指针中取得所指向的数据的类型,就要通过第二种定义方式(偏特化)。
通过这两种方法,不论对于iterator也好,还是指针也好,iterator_traits都可以取得其数据类型(其实很大程度上就是为了获取指针所指向的数据的类型,因为对于iterator来说,本来就已经定义了value_type)。