Traits技术详解
问题1:如何根据指针或者迭代器,获得指针或者迭代器所指对象的类型??
例如:
template<typename Iterator>
void func(Iterator iter)
{
//函数体
}
如果此时,要想在函数体中声明一个变量,变量的类型为迭代器所指对象的类型。
解决方法:
利用模版函数的参数类型推导机制。即根据传入的参数类型来确定模版类型的类型。如上,根据传入func()函数的类型,来确定Iterator所代表的类型。
template<typename Iterator, typename T>
void func_impl(Iterator iter, T t)
{
T temp; //这里就解决了问题
//这里做原本func()的工作
}
template<typename Iterator>
void func(Iterator iter)
{
func_impl(iter, *iter); //func的工作全部都移到func_impl里面了
}
函数func作为对外接口,实际的操作却由函数func_impl执行,通过函数func_impl的参数类型推导,获取到Iterator指向对象的类型T,从而解决了问题。
问题2:以迭代器所指对象的类型,声明返回类型
即如果需要返回类型是迭代器所指对象的类型??
解决方法:内嵌声明类型,即在迭代器内部添加一种“特性”(traits).
template<typename T>
class Iterator
{
public:
typedef T value_type; //内嵌类型声明(特性)
Iterator(T *p = 0) : m_ptr(p) {}
T& operator*() const { return *m_ptr;}
//...
private:
T *m_ptr;
};
template<class Q, class P>
typename Q::value_type //以迭代器所指对象的类型作为返回类型,长度有点吓人! !!
func(P iter)
{
return *iter;
}
int main(int argc, const char *argv[])
{
Iterator<int> iter(new int(10));
cout<<func(iter)<<endl; //输出:10
}
函数func()的返回类型前面必须加上关键词typename, 因为T是一个template参数,编译器在编译实例化func之前,对T一无所知,就是说,编译器并不知道Iterator<T>::value_type是一个类型,或者是一个静态成员函数,还是一个静态数据成员,关键词typename的作用在于告诉编译器这是一个类型,这样才能顺利通过编译。
但是这种解决方法,并不能完全解决问题,对于原生指针,也是一种迭代器,但是无法内嵌类型。对于这种问题的解决方法,是针对原生指针做特殊化处理,即利用模版偏特化(在声明特化版本之前一定要有非特化版本的声明)。
问题3:原生指针 + 迭代器萃取机
原生指针:即普通类型的指针,如int *,char *,等系统内嵌类型的指针。
利用原生指针+迭代器萃取机获得原生指针所指对象的类型。
template<class T>
class It{};
template<typename T>
class It<T*>
{
};
这个特化版仅适用于T为原生指针的情况,“T为原生指针”就是“T为任何类型”的一个更进一步的条件限制。
在STL中使用iterator_traits这个结构来专门“萃取”迭代器的特性。
Template<typename Iterator>
Struct iterator_traits
{
typedef typename Iterator::value_type value_type;
}
利用iterator_traits来萃取迭代器所指对象类型。
Template<typename It>
Typename iterator_traits<It>::value_type
Fun(It t)
{
Return *t;
}
因此,根据上述。就可以写出针对迭代器是原生指针的iterator_traits的偏特化版本。
template<class T>//声明
struct iterator_trait{};
template<class T>
struct iterator_trait<T*>//针对原生指针的偏特化处理
{//传入T类型的指针,根据模版参数推导获得T的确切类型
typedef T value_type;
};
以及利用偏特化处理的iterator_trait,所以可以改写针对原生指针的偏特化处理:
template<class T>//T为传入的原生指针
typename iterator_trait<T>::value_type
fun(T t)
{
return *t;
}
但是,如果T为const修饰的原生指针时,就会出现,当我们想用iterator_trait萃取出value_type并声明一个临时变量时,却发现声明的变量是const类型,并不能赋值(指针所指对象为const型,指针本身可变)。
为解决这个问题,我们需要一种方法区别const和非const型原生指针的偏特化版本。为此,只需要再设计一个iterator_trait偏特化版本即可。
template<class T>//声明
struct iterator_trait{};
template<class T>//针对const型原生指针的偏特化处理
struct iterator_trait<const T *>
{
typedef T value_type;
};
问题4:iterator_trait中定义的类型
STL中定义了迭代器常用的五种类型:value_type、difference_type、pointer、reference、iterator_category。
STL中iterator_traits的完整定义如下:
tempalte<typename I>
struct iterator_traits
{
typedef typename I::iterator_category iterator_category;
typedef typename I::value_type value_type;
typedef typeanme I:difference_type difference_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
};
其中:
1)value_type 迭代器所指对象的类型。
2)difference_type 两个迭代器之间的距离。以C++中ptrdiff_t为原型。
3)reference_type 迭代器所指对象的类型的引用。
4)pointer 迭代器所指的对象的相应指针。
5)iterator_category 表示迭代器的移动特性和可以对迭代器执行的操作。从iterator_category上,可将迭代器分为Input Iterator、Output Iterator、Forward Iterator、Bidirectional Iterator、Random Access Iterator.
为了保证iterator_traits可以正常工作,STL提供了一个iterator类,所有自定义的迭代器都必须继承自它,才能保证这些自定义的迭代器可以顺利的与其他STL组件进行写作。
类iterator不包含任何成员变量,只有类型的定义,因此不会增加额外的负担
参考:http://blog.csdn.net/shudou/article/details/10270971