【收藏】C++模板元编程[metaprogram]

C++模板元编程[metaprogram]by Micolai Josuttis, David Vandevoorde摘自C++ Templates: The Complete Guide一书[译者注:翻译本文,全为引介一种(相对于译者的孤陋而言)全新的编程方法。版权所有于原著者,笔者不敢稍假借之。         原文笔误甚多,族繁不可计数。笔者水平有限,改之恐失信于原著,不改恐遗害于读者。对行
摘要由CSDN通过智能技术生成

C++模板元编程[metaprogram]
by Micolai Josuttis, David Vandevoorde
摘自C++ Templates: The Complete Guide一书
[译者注:翻译本文,全为引介一种(相对于译者的孤陋而言)全新的编程方法。版权所有于原著者,笔者不敢稍假借之。
         原文笔误甚多,族繁不可计数。笔者水平有限,改之恐失信于原著,不改恐遗害于读者。对行文中的一些明显错漏,皆按自己的理解作了补正,并列原文于侧,以备查考。而文中的程序代码,未能一一核对,如有错误,还望谅解。
         原文术语,笔者自行揣摩翻译,恐失其当,多在首次出现处加译注,以方括号佐之,如:元编程[metaprogramming];译注或行文补遗之处,皆循此例。盖凡方括号中之文字,皆笔者之言也;圆括号中者为原文自有。
         本文摘自《C++ Templates: The Complete Guide》一书第17章,文中所指章节数及页数皆指原书而言。感兴趣的读者可以自行查阅原书内容。
]
        
17.1 元编程[metaprogram]的第一个例子

“元编程”指的是“编‘编程序的’程序”。换言之,我们只给出代码的布局,而编程系统则在运行时生成代码来实现我们所希望的功能。通常,“元编程”这个词意味着一种“加诸自身”的特性——元编程的组件最终会变成它的产品代码/程序中的一部分。

元编程有何吸引人之处?和其他大部分编程方法一样,元编程的目标也是为了以更少的努力换取尽可能丰富的功能——这里的“努力”同样可以用代码长度、维护成本,或者其他标准来衡量。而元编程的独特之处在于,有些用户自定义的计算工作可以在翻译期发生。[使用元编程]潜在的动机要么是为了效率(通常翻译期计算得到的东西可以被优化掉),要么是为了简化接口(元程序[metaprogram]一般都会比展开后的最终程序短小),或者二者得兼。
元编程经常依赖于第15章发展出来的traits和type functions的概念。因此,我们建议你,在研读本章之前,你应该对此前的章节了然于胸。

1 元编程的第一个例子

在1994年的C++标准化委员会开会期间,Erwin Unruh发现可以用模板[templates]在编译期执行一些计算。他[用这种方法]写了一个生成质数的程序。这个小小练习中最令人目眩神迷的部分是:质数的生成是在编译过程中由编译器完成的,而不是在运行期。特别地,编译器还为从2到某个特定值之间的每一个质数都生成了一系列的错误信息[error message]。尽管这个程序的移植性不是特别的强(因为错误信息没有被标准化),但是这个程序的确展示了模板具现化[template instantiation]机制可以作为一种初级的递归语言,用以在编译期实现一些较为复杂的计算工作的能力。这种通过模板具现化在编译期执行计算的技术通常就被称为“模板元编程[template metaprogramming]”。

为了一窥元编程的全豹,我们从一个简单的练习开始(Erwin的质数程序将在稍后的第318页展示给大家)。下面的程序展示了如何在编译期计算3的任意次方:

// meta/pow3.hpp

#ifndef POW3_HPP
#define POW3_HPP

//primary template to compute 3 to the N;
template<int N>
class Pow3 {
  public:
    enum { result = 3 * Pow3<N-1>::result };
};

//full specialization to end the recursion}
template<>
class Pow3<0> {
  public:
    enum { result = 1 };
};


#endif // POW3_HPP

模板元编程背后的驱动力是模板的递归具现化[recursive template instantiation]。在我们的程序中,为了计算3^N,我们利用下面两条规则来驱动模板的递归具现化:

1. 3^N = 3 * 3^(N-1)
2. 3^0 = 1

第一个模板实现了通常情况下的递归规则:

template<int N>
 class Pow3 {
  public:
    enum { result = 3 * Pow3<N-1>::result };
 };

当用一个正整数N来具现化这个模板的时候,模板Pow<3>必须首先计算它其中的枚举值result。这个枚举值又被定义成同一个模板被N-1具现之后的对应值。

第二个模板是一个特化版本,给出了递归的终点。它仅仅是给出了Pow3<0>时的result值:

template<>
class Pow3<0> {
  public:
    enum { result = 1 };
};

如果使用这个模板来计算3^7,只需具现化一个Pow3<7>即可。现在让我们来研究一下,当我们这样具现化这个模板的时候,具体都发生了哪些事情:

#include <iostream>
#include "pow3b.hpp"

int main()
{
    std::cout << "Pow3<7>::result = " << Pow3<7>::result
              << '/n';
}

首先,编译器具现化Pow3<7>,它的result值是:
 3 * Pow3<6>::result [此处原Pow3<5>疑为笔误,改之]
然后需要用6具现化同一个模板。依此类推,Pow3<6>将具现化Pow3<5>,后者又具现化Pow3<4>……当具现化到Pow3<0>的时候,result的值被定为1,至此递归结束。

Pow3<>这个模板(包括其特化)就被称作“模板元程序[template metaprotram]”。该程序描述了一些运算,这些运算将在翻译期随着模板具现化的进行而被执行。这个例子相对比较简单,而且也看不出对我们有多大帮助,但是到了这个地步,[元编程]这个工具已经是唾手可得的了。

17.2 枚举值[enumeration values]vs静态常量[Static Constants]

在旧式C++编译器里,要想在类声明中使用“真正的常量”(所谓的“常量表达式”[constant-expression]),枚举值是唯一的选择。但是,自从C++标准化之后,情况已经有所改变。C++标准提出了所谓“类内静态常量性初始化”[in-class static constant initializer]的概念。下面这个简单的例子介绍了这一机制的结构:

struct TrueConstants {
     enum { Three = 3 };
     static int const Four = 4;
};

在这个例子里,Four也是一个“真正的常量”——一如Three那样。

利用这个机制,我们的Pow3元程序可以像下面这样实现:

// meta/pow3b.hpp

#ifndef POW3_HPP
#define POW3_HPP


//primary template to compute 3 to the Nth


template<int N>
class Pow3 {
  public:
    static int const result = 3 * Pow3<N-1>::result;
};

// full specialization to end the recursion
template<>
class Pow3<0> {
  public:
    static int const result = 1;
};

#endif //POW3_HPP

这个版本和上个版本唯一的不同就是用类的静态常量成员代替了上个版本中的枚举值。然而,这个版本有一个缺点:静态常量成员是左值[lvalue]。因此,如果你声明这样一个函数:

void foo(int const&);

并传一个该元程序的具现结果给这个函数:

foo(Pow3<7>::result);

编译器必须把Pow3<7>::result的地址传给函数,这将强迫编译器为静态成员产生实体并分配空间。这样一来,这个计算工作的影响就不再单纯的限于“编译期”了。

枚举值不是左值(意即它没有实际的地址)。因此,即使你以“传址[by reference]”的方式调用它,也不会用到任何静态内存。传递枚举值的开销几乎完全相当于把计算的结果作为一个常数符号[literal]来传递一样。基于以上原因,在本书的所有元程序中,我们都是使用枚举值的。

17.3 另一个例子:计算平方根

让我们来看一个稍微复杂一点的例子:如何写一个元程序来计算给定值N的平方根。这个元程序应该是类似于这样的(其中用到的技术将在稍后解释)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值