[转] C++之 iterator_traits
在算法中运用迭代器时,很可能会用到其相应型别,什么是相应型别?迭代器所指之物的型别便是其中之一。C++ 有函数模版推导机制,例如:
template<class I ,class T>
void func_imp1(I iter, T t)
{
T tmp; // 这里解决了问题,T就是迭代器所指之物的型别,本例为int
};
template <class I>
inline void func(I iter)
{
func_imp1(iter ,*iter); // func 的工作全部移往 func_imp1
}
int main()
{
int i;
func(&i);
}
迭代器所指对象的型别。成为该迭代器的 value type。上述的参数型别推导虽然可用于value_type,但是当value type 必须用于函数的传回值,就束手无策了,毕竟函数的 “template 参数推导机制” 推而导之的只是参数,无法推导函数的返回值型别。
其他方法如声明内嵌型别似乎是个好主意,例如:
template<class T>
struct MyIter{
typedef T value_type; // 内嵌型别声明
T* ptr;
MyIter (T* p=0): ptr(p){ }
T& operator*() const {return *ptr;}
//....
};
template<class I>
typename I:: value_type
func(I ite)
{ return *ite; }
//...
MyIter<int>ite(new int(8));
cout<<func(ite);
注意:并不是所有的迭代器都是 class type。原生指针就不是!如果不是 class type,就无法为它定义内嵌型别。但是STL绝对必须接受原生指针作为一种迭代器,因此需要一种方法将上述的一般化概念针对特定情况做特殊化处理。
偏特化就可以做到,如下面一个class template:
template<typename T>
class C { ... }; //这个泛化版本允许T为任何型别
很容易接受它有一个形式如下的 partial specialization:
template<typename T>
class C<T*>{ ... }; //这个特化版本仅适用于“T为原生指针”的情况
// “T为原生指针” 便是 “T为任何型别” 的一个更进一步的条件限制
下面这个 class template 专门用来 “萃取”迭代器的特性,而 value type正是迭代器的特性之一:
template<class I>
struct iterator_traits{
typedef typename I::value_type value_type;
};
这个所谓的 traits,其意义是,如果 I 定义有自己的 value_type,那么通过这个 traits 的作用,萃取出来的 value_type 就是 I:: value_type.换句换说,如果I定义有自己的 value type,先前那个 func() 可以改写成这样:
template<class I>
typename iterator_traits<I>::value_type
func(I ite)
{ return *ite; }
但是除了多一层间接性,又带来什么好处呢?好处是 traits 可以拥有特化版本。现在,我们令 iterator_traits 拥有一个 partial specializations 如下:
template<class T>
struct iterator_traits<T*>{ //偏特化版本------迭代器是个原生指针
typedef T value_type;
};
最常用到的迭代器的相应型别有五种:value type,difference type,pointer,reference,iterator,iterator catagoly。如果你希望你所开发的容器能与STL水乳交融,一定要为你的容器的迭代器定义这五种相应型别。特性萃取机 traits 会很忠实的提取出来:
template<class I>
struct iterator_traits{
typedef typename I:: iterator_category iterator_category;
typedef typename I:: value_type value_type;
typedef typename I:: difference_type difference_type;
typedef typename I:: pointer pointer;
typedef typename I:: reference reference;
};