1.2.3. Type sequences
程序化的操作类型集合的能力是很多有意义的C++元程序的重要的工具
因为这种能力MPL支持的很好,这里我们仅仅简要的介绍一下基础的东西
后面,我们将重新回顾下面的例子,并演示如何使用MPL来实现
首先,我们需要一种方法来表示这个容器;一个主意是用structure来存储类型:
struct types
{
int t1;
long t2;
std::vector<double> t3;
};
不幸的是,这种安排无法使用C++给我们的编译期类型内省的能力(类似Java的Reflection)
没有办法找出成员的名字是什么,即使我们假定名字是按照上面的惯用法给出的,我们也没有办法知道有多少个成员;解决这个问题的关键是提高表示的一致性;如果我们有一个方法可以得到任何序列的第一个类型,和剩余的序列,那么我们将轻易的获取所有成员:
template< typename First, typename Rest >
struct cons
{
typedef First first;
typedef Rest rest;
};
struct nil {};
typedef
cons<int
, cons<long
, cons<std::vector<double>
, nil
> > > my_types;
上面的my_types所描述的结构是单向链表的编译期对应物,是由Czarnecki and Eisenecker in [CE98]首先引进的;现在,我们已经调整了最初的结构,此时C++模板机制能够一层层的剥开它,我们来看一个完成这个功能的简单的metafunction;假设用户希望找到任意类型集合中最大的一个类型;我们可以使用现在已经很熟悉的递归的metafunction:
Example 1. 'largest' metafunction
// choose the larger of two types
template<
typename T1
, typename T2
, bool choose1 = (sizeof(T1) > sizeof(T2)) // hands off!
>
struct choose_larger
{
typedef T1 type;
};
// specialization for the case where sizeof(T2) >= sizeof(T1)
template< typename T1, typename T2 >
struct choose_larger< T1,T2,false >
{
typedef T2 type;
};
// get the largest of a cons-list
template< typename T > struct largest;
// specialization to peel apart the cons list
template< typename First, typename Rest >
struct largest< cons<First,Rest> >
: choose_larger< First, typename largest<Rest>::type >
{
// type inherited from base
};
// specialization for loop termination
template< typename First >
struct largest< cons<First,nil> >
{
typedef First type;
};
int main()
{
// print the name of the largest of my_types
std::cout
<< typeid(largest<my_types>::type).name()
<< std::endl
;
}
这段代码中有几个地方值得注意:
• 它使用了一些ad-hoc,深奥的技术,或者“hacks”
缺省模板参数choose1(用“hands off!”做了标记)就是一个例子;没有它,我们将需要另外一个模板来提供choose_larger的实现,或者我们不得不显式的计算后作为参数提供给模板,对这个例子也许不算太坏,但它将使choose_larger用处更少,更加容易出错
另一个hack是从choose_larger中分出特化的largest;这是一种减少代码的措施,将使程序员避免在模板体中编写“typedef typename ...::type type”等
• 即使这么简单的元程序也使用了三个单独的偏特化
largest metafunction使用了两个特化;有人可能期待着这意味着有两个结束条件,但不是这样的:其中一个特化只是用来简单的处理对序列元素的存取;这些特化通过将单个metafunction的定义散布在几个C++模板的定义中而使代码难以阅读;并且,因为它们是偏特化,对于C++社区中其编译器不支持偏特化的广大的程序员来说,它们是不可用的
然而,这些技术理所当然是任何好的C++元程序员武器库中很有价值的部分;通过封装常用的结构,内部处理循环结束,MPL减少了tricky hacks和模板特化的需要