C++模板有时候会收获意外之喜,元编程(meta-program)或许可以算一个吧。什么东西一粘上元(meta),感觉瞬间就带上了某些神秘的色彩,Facebook不就改名为Meta了吗?

从前面的介绍我们知道对于模板,编译器会在编译时按需完成模板类型的实例化,这个过程其实就是把我们的模板代码当作输入,根据定义产生新的代码,而这些新的代码才是真正实现我们所期望的功能。

有些聪明的人很快就想到了其实可以控制这个代码生成过程,而不仅仅是让编译器完成类型参数或非类型参数的实例化。不过这种内嵌在C++模板机制上的原生语言元编程能力的发现却是从一次编译失败的错误信息中反馈出来的:Erwin Unruh在利用编译期执行整数计算时,诱导编译器给出了包含素数列表的错误信息。

c++类模板_综合应用能力c类作文模板_c++模板类

C++的元编程能力是图灵完备的:模板实例化机制本身是一种基本的递归语言机制,可以实现循环;模板特化可以满足分支路径的选择。这种编译期的部分称为“纯函数性语言”,它没有任何可变的东西,只有定义。所以一开始接触你可能会有一点不习惯,需要你改变一下思考方式:所有东西都是不变的,所有循环和分支都是提供给编译器使用的,由编译器在需要时产生一个新的定义。最后得到的是你真正需要的那个定义。

说了这么多,还是看个简单的例子吧,元程序中的计算数值。

template
struct Pow2 {
    static constexpr std::size_t value = 2 * Pow2::value;
};
template
struct Pow2 {
    static constexpr std::size_t value = 1;
};
Pow2::value; // 8

上面的代码基本上就是数学定义的实现,非常简单。2的N次方等于2*2的N-1次方,N为0时,值为1。当我们需要Pow2这个定义时,编译器会递归定义Pow2和Pow2,在Pow2时不再定义了,因为已经有现成的了。这个Pow2::value是个编译期值,可以用在如数组长度的定义中,int arr[Pow2::value]。

可以看出静态C++代码和动态C++代码是完全不同的,前者是函数式的,没有变量,没有赋值语句c++模板类,没有迭代,只有定义;后者是命令式的,由你负责驱动状态的不停转换。

下面再看一个例子,这回我们计算类型。

template
struct IF {
    using type = Then;
};
template
struct IF {
    using type = Else;
};
IF::type i;

定义很简单,但很有用,通过某个编译期的条件,我们可以得到某个类型(这个模板STL已经给咱们提供了,struct conditional)。

下面我们简单总结一下C++中的静态子程序设计的一些特性:

类模板当作函数(元函数),作为编译时的函数,模板参数列表中的类型、整数作为参数,通过定义整数或类型作为返回值,完成数值计算或类型计算。整数和类型作为数据,这两个恰好都是模板参数列表中的可用数据类型(这个当然不是巧合),我们还可以通过嵌套类模板来表示更加复杂的数据结构,比如说Type list,或Value list,这两个模板可以说非常基础,它们的应用非常广泛c++模板类,后面找时间我们详细讲解一下实现。定义代替变量,静态层次的程序设计其实只是定义,没有状态改变,都是纯函数,没有任何副作用,当你需要一个新的状态时,很简单,定义一个。模板的递归构造代替循环。不过要小心控制你的程序,编译器能够使用的资源是有限的,在实例化某个模板时,由于递归的使用,中间会不断地挂起,转而去实例化嵌套的模板,例如实例化Pow2的过程中,会实例化Pow2,中间会挂起先去实例化Pow2,然后回转实例化操作。如果中间挂起的数目过多(如Pow2,会一直挂起到实例化完成Pow2),编译器会停止工作,直接报错(C++标准要求编译器至少支持17层的嵌套,当然绝大多数编译器都会支持的更多,据说可以到1024层)。条件操作符和模板特化作为条件分支,通常也是结束循环的一种方式。

限时特惠:本站每日持续更新海量设计资源,一年会员只需29.9元,全站资源免费下载
站长微信:ziyuanshu688