GNU C++ (MinGW Special)的下载、使用以及模板元编程……

荣耀  2003

我在编写C++程序和测试C++特性时,最常使用GNU C++ (MinGW Special)(以下简称G++)、BCB5、VC7和Digital Mars四种编译器。我的经验是,在面向对象领域,这几种编译器对标准的实现难分高下,但在模板尤其是高级模板特性领域,G++ 3.2表现最为出色(当然也谈不上完美)。

G++是GCC中的一员。GCC的含义是GNU编译器集合(GNU Compiler Collection),包括C、C++、Objective-C、Fortran、Java、Ada连同与这些语言相配的库等。GCC被誉为 “世界上最重要的软件”。许多免费的编译器都是GCC的移植版 — MinGW(Minimalist GNU for Windows)中携带的编译器就是如此。在写这篇文章时(2003/9/23),GCC的最新版本是3.3.1,而最新版的MinGW 3.1.0支持的GCC版本是3.2.3。(注:如欲了解GCC和MinGW正式完整的描述,请访问http://gcc.gnu.orghttp://sourceforge.net/projects/mingw

http://sourceforge.net/projects/mingw提供了详细的文件下载列表。如果你暂时无法确定该下载哪些文件,下载MinGW-3.1.0-1.exe即可(其它文件待你弄明白后视需要下载)。这个文件大小为14.5 MB,除了包含有一组C/C++编译器外,还带有一个用于编译FORTRAN 77的g77编译器以及其他一些辅助工具和文件。下载后直接安装即可,无需任何额外的配置。当然,你也可以为编译器设置一个PATH环境变量(例如c:\mingw\bin;)。这种一劳永逸之举多少可为自己日后省点麻烦。

我们的兴趣在于G++。它的用法非常简单:

g++ -o dftest.exe dftest.cpp delimfile.cpp

键入g++ --help命令即可查看完整编译和连接选项。

在1994年C++标准委员会的一次会议期间,Erwin Unruh展示了一个可以产生质数的程序。此程序特别之处在于质数的产生发生于编译期而非运行期。在编译器产生的一系列错误信息中间夹杂着2到某个设定值之间的所有质数。它展示了可以利用模板实例化机制于编译期执行一些计算。这种通过模板实例化而执行的编译期计算技术,即是广为人知的模板元编程(Template Metaprogramming)。

当年Erwin Unruh凭借Metaware编译器展示了这一有趣的效果,幸运的是,今天触手可及的G++还能为我们完整再现之。也怪不得其他几款编译器,因为编译器的出错信息并未被标准化。

下面就是94年那次会议期间广为流传的代码之修订版(为了能够在今天符合标准的编译器上进行编译,做了少许修改):

// Prime number computation by Erwin Unruh 

template <int i> struct D { D(void*); operator int(); }; 

template <int p, int i> struct is_prime { 
  enum { prim = (p==2) || (p%i) && is_prime<(i>2?p:0), i-1> :: prim }; 
}; 

template <int i> struct Prime_print { 
  Prime_print<i-1> a; 
  enum { prim = is_prime<i, i-1>::prim }; 
  void f() { D<i> d = prim ? 1 : 0; a.f();} 
}; 

template<> struct is_prime<0,0> { enum {prim=1}; }; 
template<> struct is_prime<0,1> { enum {prim=1}; }; 

template<> struct Prime_print<1> { 
  enum {prim=0}; 
  void f() { D<1> d = prim ? 1 : 0; }; 
}; 

#ifndef LAST 
#define LAST 18 
#endif 

main() { 
  Prime_print<LAST> a; 
  a.f(); 
}

因为类模板D只有一个参数为void*的构造器,而只有0才能被合法转换为void*,因此,在g++上编译这段程序时,编译器将会给出如下错误信息(以及其他一些信息。简短起见,它们被删除了):

unruh.cpp:12: invalid conversion from `int' to `void*'
unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 17]'
unruh.cpp: In member function `void Prime_print<i>::f() [with int i = 13]':
unruh.cpp:12: invalid conversion from `int' to `void*'
unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 13]'
unruh.cpp: In member function `void Prime_print<i>::f() [with int i = 11]':
unruh.cpp:12: invalid conversion from `int' to `void*'
unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 11]'
unruh.cpp: In member function `void Prime_print<i>::f() [with int i = 7]':
unruh.cpp:12: invalid conversion from `int' to `void*'
unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 7]'
unruh.cpp: In member function `void Prime_print<i>::f() [with int i = 5]':
unruh.cpp:12: invalid conversion from `int' to `void*'
unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 5]'
unruh.cpp: In member function `void Prime_print<i>::f() [with int i = 3]':
unruh.cpp:12: invalid conversion from `int' to `void*'
unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 3]'
unruh.cpp: In member function `void Prime_print<i>::f() [with int i = 2]':

C++模板元编程技术在Blitz++[http://www.oonumerics.org/blitz/]这样的数值矩阵库中得到了进一步的应用。如果你对编译期计算和模板元编程技术感兴趣,我推荐你阅读《C++ Templates: The Complete Guide》一书。幸运的是,内地和台湾年内都将出版这部著作的中文版。

-完-