萃取

Traits 编程技法(萃取)

不论是泛型思维还是STL的实际运用,迭代器都扮演着重要的角色。迭代器是一种行为类似指针的对象,在算法中运用迭代器时,很可能会用到其相关类型。其中的相关类型就包括迭代器所指的对象的类型。假设算法中有必要声明一个变量,其类型就是迭代器所指的对象的类型,但是输入参数却只有迭代器改怎么办?
c++并没有支持typeof()!

我们假设现在要实现一个算法func,其出入参数为迭代器

void func(Iter iter)
{
	//如果我们需要一个iter指向的类型的变量,
	//该怎么声明呢?
}

方法一:参数推导

利用函数模板的参数推导机制(argument deducation)

template 
void fun_impl(I iter, T t)
{
	T tmp; //这里已经申请好了需要的变量
	//剩余的内容
	//...
}

于是,func函数就可以这么写:

void func(Iter iter)
{
	func_impl(iter, *iter); //将*iter传进去后,就知道了iter指向的类型
}
	

func函数将实际操作全部置于func_impl()之中,由于func_impl是一个函数模板,一旦被调用,编译器会自动进行template参数推导,于是导出类型T。

但是,当我们需要func的返回值是*iter的类型而不是void呢,我们连函数声明都写不出来!

返回值类型(不知道怎么写) func(Iter iter)
{
	func_impl(iter, *iter);
	...
}

注意:参数推导机制无法推导返回值

方法二:声明内嵌类型

template 
struct Iter{
	typedef T value_type;
	//...
}
template 
typename I::value_type func(I iter)
{
	return *iter;
}

这样就成功写出了func函数。
看起来不错,但是却有个隐晦的陷阱:并不是所有迭代器都是class type。原生指针就不是!如果不是class type,就无法为它定义内嵌类型。但STL(以及整个泛型思维)绝对必须接受原生指针作为一种迭代器,所以上面这样还不够,有没有办法可以让上述的一般化概念针对特殊情况做特殊化处理呢?template partial specialization(偏特化)可以做到。

方法三:萃取器

下面这个类模板专门用来萃取迭代器的特性,而value_type正是迭代器的特性之一。

template 
struct iterator_traits
{
	typedef typename I::value_type value_type;
	//...
}

我们可以通过这个萃取器萃取类型I的特性,换句话说,如果类型I定义了自己的value_type,那么iterator_traits::value_type就是I::value_type。

如方法二定义的Iter结构,Iter中定义了自己的value_type,那么func就可以改写成:

template 
typename iterator_traits::value_type func(I iter)
{
	return *iter;
}

但这除了多了一个中间层iterator_traits,又带来什么好处呢?
好处就是,iterator_traits可以拥有特化版本。现在,我们令iterator_traits拥有一个partial specializations如下:

template 
struct iterator_traits //偏特化版,迭代器是个原生指针
{
	typedef T value_type;
};

于是当我们调用func传入的是原生指针时,通过偏特化版本同样可以萃取到value_type。
到此,我们就基本解决了迭代器的相关类型的问题了!

迭代器相关类型

根据经验, 最常用到的迭代器相关类型有五种:value_type, difference_type, pointer, reference, iterator_catagoly。如果你希望你所开发的容器能与STL水乳交融,一定要为你的容器的迭代器定义这五种类型。具体内容参照STL源码剖析的相关章节。

你可能感兴趣的:(c++,c++,c语言,后端)