traits编程技法
模板函数的参数推导机制:
eg:
template<typename I, typename T>
void func(I iter, T t)
{
T tmp;
...;
}
void main()
{
int i;
func(&i,i);//模板函数可以通过参数的推导机制推导出实际的函数为void func
//(int*,int);
}
可能有的人看到模板函数之后会高兴的手舞足蹈,但是该技巧虽然可用于value_type,但是却非全面
可用。玩意value_type必须用于函数的传回值,就束手无策了,毕竟模板参数推导机制推导的仅仅是
参数而已,无法推导函数的返回值型别。
这就造成一个严重的问题,试想一个算法如果需要返回value_type型别的返回值,通过模板参数推导
机制是无法实现的。在这种情况下,我们何不在迭代器的实作当中将该类型保存起来呢??既然如此,
我们来看看c++标准库的实作手法。
template<typename T>
struct MyIter
{
typedef T value_type;//内嵌型别声明
T *ptr;//迭代器实体
MyIter(T* p=0):ptr(p){}
T& operator*(){return *ptr);
}
//下面为实作算法,返回型别为迭代器所指对象的型别
template<typename I>//I在此应该为一种迭代器型别
typename I::value_type
GetValue(I)
{
return* I;//返回迭代器所指的对象
}
注意:GetValue的回返型别必须加上关键词typename,因为T是一个template参数,在它被编译器具
现化之前,编译器对T一无所悉,换句话说,编译器并不知道MyIter<T>::value_type代表的是一个型
别或是一个member function或是一个data member。关键词typename意在告诉编译器这是一个型别,
如此才能通过编译。
但是这里有一点我们还没有考虑到,我们在这里假设的是迭代器是一种类类型,classtype,但是所
有人都因该知道仅仅这是不够的,因为原生指针就不是如此,STL必须接受原生指针作为一种迭代器
,于是就出现了偏特化
eg:
template<typename T>
class C{};//这个泛化版本允许接受T为任何型别
template<typename T>
class C<T*>{};//该泛化版本仅适用于"T为原生指针"的情况
回忆一下,我们已经定义了迭代器,算法,再看看萃取器
template<typename T>//萃取器
struct iterator_traits
{
typedef typename T::value_type value_type;
}
template<typename T>//算法
typename iterator_traits<T>::value_type
func(T iter)
{
return *iter;
}
可能有人会说,萃取器比前面没用萃取器多什么,仅仅是多了一个层次的封装,其实除了这个意义外
,好处是traits可以拥有特化版本,哈哈。。。
eg:
template<typename T>
struct iterator_traits<T*>
{
typedef T value_type;
}
template<typename T>
struct iterator_traits<const T*>
{
typedef T value_type;
}
在此,有了萃取器,我们可以很容易的萃取出迭代器的响应型别,当然了,首先你必须遵循迭代器的
定义规则,必须将响应的内嵌型别定义出来。
最常用的迭代器响应型别有5种。
template<typename T>
struct iterator_traits
{
typedef typename T::value_type value_type;
typedef typename T::pointer pointer;
typedef typename T::reference reference;
typedef typename T::difference_type difference_type;
typedef typename T::iterator_category iterator_category;//这是一个很重要的型别,
对于算法来说很重要,有时候不同的迭代器在算法中采取的是不同的版本,这样在算法里面就需要进
行判断,如果用if语句来判断,由于这样会在执行期进行判断影响效率,于是在算法中进行重载函数
将该变量作为一个参数传递
}
最后,我们来总结一下c++标准库的发展过程:
1.函数模板参数推导机制,完成了参数的推导,无法对返回型别进行推导
2.为了解决前述问题,出现了内嵌型别,将迭代器型别嵌入到一个称为类类型的结构体中,但无法解
决原生指针的特殊问题,因为原生指针不是类类型
3.于是出现了模板的偏特化
总结:traits编程技法大量运用于stl实现中,它利用“内嵌型别”的编程技巧与表一起的template
参数推导功能,增强c++未能提供的关于型别认证方面的能力,弥补c++不为强型别语言的遗憾。