首页 » 技术分享 » C++中的萃取原理

C++中的萃取原理

 

 萃取(traits)

    它主要解决的问题是相应型别(associated type),迭代器所指之物的型别便是其一。若要以“迭代器所指对象的型别”来声明一个对象,如果如果没有traits ,解决办法是:利用function

template的参数推导(argument deducation)机制。

如图:

 

我们以func()为对外接口,却把实际操作全部置于func_impl()之中,由于func_impl()是一个function template,一旦被调用,编译器会自动进行template参数推导。预算导出类型T,顺利解决问题。但是迭代器相应型别有五种,然而并非任何情况下任何一种都可利用上述的template参数推导机制来取得,但是我们往往需要更全面的解法。

  上述方法的缺陷是万一 value type 必须用于函数的返回值,就束手无策了,毕竟函数的“tmplate参数推导机制”推而导之的只是参数,无法推导函数的返回值类型。

我们需要其他方法。声明内嵌型别似乎是一个好主意,像这样:

template<class T>
struct MyIter
{	typedef T value_type;
	T*ptr;
	MyIter(T *p= 0):ptr(p){}
	T& operator*()const {return *ptr;}
};

template<class T>
typename T::value_type
	func(T ite){return *ite;}


void main()
{
	MyIter<int> ite(new int(8));
	cout<<func(ite);
}

 

在此列子中,有个隐晦的陷阱:并不是所有的迭代器都是class type。原生指针就不是!

 

如果不是class type,就无法为它定义内嵌型别。但STL(以及整个泛型思维)绝对接收原生指针作为一种迭代器,所以上面这样还不够。有没有办法可以让上述的一般化概念针对特定情况(例如针对原生指针)做特殊处理呢?template partial specialization可以做到。

Partial Specialization(偏特化)的意义:

如果class template 拥有一个以上的template参数,我们可以针对其中某个(或数个,但非全部)template参数进行特化工作。换句话说,我们可以在泛华设计中特工一个特化版本(也就是将泛化版本中的某些template参数赋予明确的指定)。

    有了这项利器,我们便可以解决前述“内嵌型别”未能解决的问题。先前的问题是,原生指针并非class,因此无法为它们定义内嵌型别。现在,我们可以针对“迭代器之template参数为指针”者,设计特别版的迭代器。

下面这个class template专门用来“萃取”迭代器的特性,而value type正是迭代器的特性之一:

#include<iostream>
using namespace std;
template<class T>
struct	iterator_traits//traits 意为“特性”
{
	typedef typename I::vlue_type value_type;
};

 

这个所谓的traits,其意义是,如果I定义自己的value type,那么通过这个traits的作用,萃取出来的value_type就是I::value_type。换句话说,若果I 定义有自己的value type ,先那个func()可以改写成这样:

 

template <class T>
typename iteraotr_traits<T>::value_type//这一整行是函数返回值
	func(T ite)
{    
    return *ite;
}

但这除了多了一层间接性,好处是traits可以拥有特化版本。现在,我们令iterator_traites拥有一个partial specializations如下:

template<class>

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相同,而现在,声明一个无法复制的暂时变量,没什么用!因此,如果迭代器是一个pointer-to-const,我们应该设法令其value type为一个non-const型别。只需要另外设计一个特化版本就可以解决问题:

template<class T>
struct iterator_traits<const T*>{//偏特化版—当迭代器是一个pointer-to-const
typedef T value_type;//萃取出来的型别应该是T,而非const T
};

现在,不论面对的是迭代器MyIter,或是原生指针*int 或const int*,都可以通过traits取出正确的(我们所期望的)value type。

下图说明了traits所扮演的“特性萃取机”角色,萃取各个迭代器的特性。这里所谓的迭代器特性,指的是迭代器的相应型别。当然,若要这个“特性萃取机”traits嫩够有效运作,每一个迭代器必须遵守约定,自行以内嵌型别定义的方式定义出相应型别。这是一个约定,谁不遵守约定,谁就不能兼容STL这个大家庭。

 

 

摘自《STL源码剖析》。

 

转载自原文链接, 如需删除请联系管理员。

原文链接:C++中的萃取原理,转载请注明来源!

0