谈C++模板元编程必然要谈的是类型推理。因为C++的内置类型非常简单(这里不考虑STL),并不需要用到复杂的类型推理机制也能良好的使用,但是在具体项目开发中往往需要对自定义类型添加类型推理,这里就对这个问题进行探讨。
- 直接类型推理
直接类型推理,就是给定模板类型参数,返回类型参数,我们用模板类来实现。类型推理可以看成定义在类型空间上的运算,包括一元运算,二元运算或者更多参数类型。利用模板类和typedef
以及辅助的宏定义可以实现直接类型推理。下面是一个单参数类型推理:
struct Nulltype {};
template<typename T>
struct TypeInferenceT
{
typedef Nulltype type; // default
};
#define __DEFINE_TYPE_INFER__(T, T1) 、
template<> \
struct TypeInferenceT<T> \
{ \
typedef T1 type; \
};
// ...
__DEFINE_TYPE_INFER__(int, float)
__DEFINE_TYPE_INFER__(float,double)
// ...
下面是二元参数类型推理,相对来说更常用,比较适合二元函数的类型推理,我们这里直接用一个二元函数(类中的静态函数)来举例:
template<typename T>
struct TypeIdT
{
static const int value = -1;
};
template<int N>
struct TypeT
{
typedef Nulltype type;
};
#define __DEFINE_TYPEID__(T, ID) \
template<> struct TypeIdT<T> { static const int value = ID; }; \
template<> struct TypeT<ID>{ typedef T type; };
template<typename T1, typename T2>
struct Op2
{
static const id1 = TypeIdT<T1>::value;
static const id2 = TypeIdT<T2>::value;
static const id = (id1>id2)?id1:id2
typedef typename TypeT<id>::type type;
static type func(T1 v1, T2 v2) { /* ... Implementation ... */ }
};
上述代码的意义如下:
- 利用两个模板类和一个宏,将一个整数id和类型一一对应起来
- 在模板类Op2<T1,T2>中利用编译期常量作为推导依据,获得两个类型中id值较大的那个类型,作为静态成员函数func(T1,T2)的返回值类型
- 部分模板匹配的直接类型推理
利用部分模板匹配技术,实现对一批类型的匹配。例如:
template<typename T>
struct MyTemplateClass1T { /* ... */ };
template<typename T>
struct TypeIdT<MyTemplateClass1T<T> >
{
static const int value = TypeIdT<T>::value + 1024;
};
上述类型推理的效果是对于所有的类型T,都有MyTemplateClass1T<T>的类型ID号都可以从T的类型ID号增加1024计算得到。
这个技术对自定义模板类非常有效,如自定义复数类型、向量类型、矩阵类型等等。
- 更复杂的类型推理:高阶类型推理
高阶类型其实是指参数类型(或像前面说的类型函数),对C++型而言就是类模板,例如前面的
MyTemplateClass1T
就是个带一个类型参数的高阶类型
高阶类型也能参与类型推理(并不是指部分模板参数匹配技术)
例如我们需要将两个参数模板类型符合起来:
template<template<typename>class U>
struct FunctorT
{
template<typename T1, typename T2>
struct impl
{
typedef T2(*func_t)(T1);
static U<T2> fmap(func_t func, const U<T1>& ut1)
{
U<T2> ut2;
ut2.resize(ut1.size());
for(size_t i=0; i<ut2.size(); ++i)
{
ut2[i] = func(ut1[i]);
}
return ut2;
}
};
};
上述模板类型利用高阶类型(
U<T>
)作为输入参数,得到一个泛化的模板类(FunctorT<U>::impl<T1,T2>),该类型可以用来实现函数式中的fmap(默认实现仅对包含.size和.operator[]接口的类型U有效)。
- 其他推理
其他C++类型推理技术主要就是利用前面两种推理机制通过组合构造来产生更复杂的类型推理机制,其中有一些模板元编程常用的辅助类型推理工具,如:
template<bool cond = false, typename T1, typename T2>
struct meta_if { typedef T2 type; }
template<typename T1, typename T2>
struct meta_if<true,T1,T2> { typedef T1 type; };
template<typename T1, typename T2>
struct Cons
{
typedef T1 type1;
typedef T2 type2;
};