Metafunction: compile-time analogs of runtime functions
(元函数:编译期的函数)
与运行期使用函数封装算法相对应,在C++编译期同样存在着封装算法进行编译期计算的需要。元函数(metafunction)便是实现这一需求的主要手段。元函数这一概念最早是由Boost.MPL类库提出并定义的。
作为在编译期进行计算的函数,元函数具有以下不同于运行期函数的特点:
- 输入(即参数)与输出(即返回值)均只包含两种类型:1)类型(名)2)整形常量。
- 可以返回一个或多个值,但不能没有返回值。
- 没有副作用:元函数在计算过程中既不能改变参数的值,也不具备“向控制台输入输出”之类的附加功能。
Kinds of Metafunction(元函数的分类)
按照返回值的类型来划分,元函数可分为以下两大类:
1. type型元函数:返回类型(名)的元函数,也就是在编译期进行类型计算的元函数。
/****** 代码1 ******/
template <bool B, class L, class R>
struct IF
{
typedef R type;
};
template <class L, class R>
struct IF<true, L, R>
{
typedef L type;
};
IF<false, int, long>::type i; // is equivalent to long i;
IF<true, int, long>::type i; // is equivalent to int i;
代码1定义了一个用于编译期类型选择的元函数IF,其实现依赖于类模板IF及其特化。
元函数IF的输入由3个模板参数构成:布尔类型B,类型名L,类型名R。
元函数IF的输出也就是需要返回的类型由typedef语句来定义,用于返回的类型名为type。
此处用于返回的类型名取名为type是为了遵循Boost.MPL类库的惯例。
若B的值为true,则元函数IF返回类型名L。(此分支由模板特化来定义)
若B的值为false,则元函数IF返回类型名R。(此分支由主模板来定义)
此处用struct关键字而不是class关键字来定义类模板的理由是:元函数IF的返回类型必须能被外界所访问,即type的访问权限必须是public。
2. value型元函数:返回整形常量的元函数,也就是在编译期进行常量计算的元函数。
/****** 代码2 ******/
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
//static const int value = N * Factorial<N - 1>::value;
};
template <>
struct Factorial<0>
{
enum { value = 1 };
//static const int value = 1;
};
// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
int x = Factorial<4>::value; // == 24
int y = Factorial<0>::value; // == 1
}
此码2定义了一个用于编译期计算阶乘的元函数Factorial,其实现依赖于类模板Factorial及其特化。
元函数Factorial的输入仅由1个模板参数构成:整数类型N。
元函数Factorial的输出也就是需要返回的整形常量由枚举或静态整形常量来定义,用于返回的常量名为value。
此处用于返回的常量名取名为value是为了遵循Boost.MPL类库的惯例。
若N的值为0,则元函数Factorial返回1。(此分支由模板特化来定义)
若N的值大于0,则元函数Factorial返回N*Factorial(N-1)。(此分支由主模板来定义)
此处用struct关键字而不是class关键字来定义类模板的理由是:元函数Factorial的返回值必须能被外界所访问,即value的访问权限必须是public。
Metafunction with "Multiple returns"(返回多值的元函数)
与运行期的函数有所不同,编译期所定义的元函数也可返回不止一个值。
Boost.MPL类库中的Integral Constant Wrapper(整形常量包装器)就属于这种返回多个类型名及整形常量的元函数。
/****** 代码3 ******/
template< bool x > struct bool_
{
static bool const value = x;
typedef bool_<x> type;
typedef bool value_type;
operator bool() const { return x; }
};
typedef bool_<false> false_;
typedef bool_<true> true_;
代码3(取自Boost.MPL类库)实现了一个布尔常量的包装器。
元函数bool_共有3个返回值。
1) 布尔值value。
2) 元函数自身的类型type。
3) 布尔值value的类型value_type。
根据元函数bool_的定义,不难推出以下等式为真。
1) false_::value == false。
2) true_::type::value == true。
Nullary Metafunction(零参数元函数)
元函数也可不用类模板而用普通的类来定义,此时元函数的参数个数为0,即元函数没有输入只有输出。
/****** 代码4 ******/
struct always_int
{
typedef int type;
};
Member Metafunction(“成员”元函数)
元函数也可在类或类模板中定义,即元函数通过嵌套类的方式来实现。
这种元函数可称作“成员”元函数,也可称作“元方法”。
/****** 代码5 ******/
template <typename T>
struct value
{
BOOST_STATIC_ASSERT(
mpl::not_<is_reference<T> >::value != 0);
typedef mpl::false_ no_nullary;
template <typename Env>
struct result
{
typedef T type;
};
value(T const& arg)
: val(arg) {}
template <typename Env>
T const&
eval(Env const&) const
{
return val;
}
T val;
};
代码5(取自Boost.Pheonix类库)中,类模板value总共定义了2个元方法。
1) value型元方法no_nullary。
这里no_nullary被定义为false_的同义词,false_的定义可参见示例代码3。
2) type型元方法result。
Higher-order Metafunction(高阶元函数)
元函数的参数也可以是另一个元函数,这种元函数被称作高阶元函数。Boost.MPL类库中的transform就属于高阶元函数。
Metafunction vs Function(元函数 vs 函数)
编译期“元函数” | 运行期“函数” | |
是否允许返回多值 | 是 | 否 |
是否允许不返回值 | 否 | 是 |
是否存在副作用 | 否 | 是 |
是否允许零参数 | 是 | 是 |
能否作为类成员 | 是 | 是 |
是否存在高阶函数 | 是 | 是 |