一般现象:在算法中应用迭代器时,有时候会用到其所指对象的相应类型。
引出问题:假设算法中有必要声明一个变量,以迭代器所指对象的类型为类型,如何解决?
解决方法:
利用函数模板的参数推导技术(function template argument deducation)
例如:
template<class I, class T>
void func_impl(I iter, T t)
{
T tmp; //这里解决了问题,T就是迭代器所指对象的类型 本例 为int
// 。。。这里做func() 应该做的全部工作。
T
}
template <class I>
inline void func(I iter)
{
func_impl(iter , *iter);//func的工作全部移往func_impl
}
int main()
{
int i;
func(&i);
}
我们以func() 作为对外接口,把实际的操作全部置于func_impl中 ,由于func_impl是一个函数模板 一旦被调用 编译器会自动进行 模板参数推导
于是导出类型T 顺利结觉问题。
--------------------------以上我们只解决了迭代器相应类型的一种------
由此我们引出 Traits编程
善于发现问题的读者已经看到了上一个例子中我们用函数模板推导出的所指对象类型只能用于函数的传入参数,却不能做函数的返回参数。
解决这个问题:方法:声明内嵌类
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 是一种数据类型而不是即将传入的类的成员函数或者是成员变量。
(:typename用法)
typenmae I::value_type func(I iter)
{
return *iter;
}
MyIter<int> iter(new int(8));
cout<<func(iter);
----------我们解决了这个问题 -----------------------------------------
读者有没有发现我们上面的迭代器是一个类类型,只有类型才可以用上面的内嵌类方法,可是不是所有的迭代器都是class type
解决问题:Partial Specialization(偏特化)
类模板(class一 template)拥有一个以上的模板参数,我们可以针对其中某个或者数个 但不能是全部(如果是全部的话就成了一个全特化)
template参数做进一步的特化工作。
有必要对偏特化进行更进一步的解释:
例:template<class U,class V,class T>
class C
{
};
偏特化并不是多模板参数(U,V,T)中的一个或者几个的组合指定某个参数值,所谓的偏特化意思是提供另一份template 定义式,而其本身仍为templatized。
《泛型思维》对偏特化的定义:针对(任何)template参数更进一步的条件限制设计出来的一个特化版本。
例 template<typename T>
class C
{
};
template<typename T>
class C<T*>
{
};
于是我们对之前存在的问题进行解决
先前的问题是 原生指针并非类类型,因此无法为他们定义内嵌类型, 现在我们可以针对“迭代器template参数为指针”,设计特化版的迭代器。
下面的类模板专门用来“萃取”迭代器的特性 而迭代器所指类型正是其特性之一
template<class I>
struct iterator_traits
{
typedef typename I::value_type value_type;
}
如果 I 定义有自己的value_type 那么通过这个traits的作用 萃取出来的value_type 就是I::value_type 换句话讲如果I定义有自己的value_type
先前那个func()可以改写成:
template<class T>
typename iterator_traits<I>::value_type func(I iter)
{
return *iter;
}
对比一下:
//typenmae I::value_type func(I iter)
//{
// return *iter;
//}
读者可以看出
template<class I>
struct iterator_traits
{
typedef typename I::value_type value_type;
}
是一个间接层 还有什么好处呢 这个间接层
好处是traits可以拥有特化版本例
tempalte <class T>
struct iterator_traits<T *>//偏特化迭代器是一个原生指针
{
typedef T value_type;
}
于是int* 虽然不是一种class type 亦可以通过traits取其value_type。这就解决了先前的问题。
---------------------我们已经解决了这个问题----------------------------
但是又引出一个问题:
指向常数对象的指针(pointer-to-const)
iterator_traits<const int*>::value_type
获得是一个const int 而不是int 不是我们期望。用获得这种value_type 声明的临时变量因const 的原因无法赋值 因此将变得毫无意义
因此 如果迭代器是一个pointer-to-const 我们要设法使其value_type 为一个non-const
解决方法:
template<class T>
struct iterator_traints<const T*>
{
typedef T value_type;
}