C++ 模板元编程学习心得体会

快速翻了一遍传说中的、大名鼎鼎的 modern c++ design,钛合金狗眼顿时不保,已深深被其中各种模板奇技淫巧伤了身。。。论语言方面的深度,我看过的 c++ 书里大概只有 insight c++ object model 能与之一战吧?难怪 Herb 老喜欢调侃 Andrei 在模板方面是个可怕的家伙,就从这本书的质量来看,Andrei 当之无愧。

c++ 模板元编程的力量远比第一眼印象里所能想像得要强大,当然,这个结论并不明显,很多时候人们也就拿模板当作减少重复代码的工具简单使用,很少有人会像写 STL, boost, loki 那样子正儿八经完全以模板为根基进行创作,个中原因是多样的,一个结果就是,模板的威力即使在很多 c++ 熟手的手下也不容易充分展现出来,当然从工程的角度来讲,这并非就是坏事,并不见得非得穷尽语言的高级特性,写出让人惊叹的代码才是好代码,实战中更多时候讲究的是简洁,易读,好上手好维护这些基本原则,特别是当团队中人员在语言水平上参差不齐时更是如此,杜甫的诗也许更工整严谨,但白居易的诗则显然更老少皆宜,这个体会我是在阅读 boost.proto 的代码时得来的,好奇的读者可以自行去了解一下,注意做好安全措施。

从语言特性上来说,模板元编程具备了一个完备的编程语言所必需的一些基本结构,比如说,循环,分支,判断等,当然这些结构在模板中可能也不太明显,例如循环,它的实现关键在于使用递归,出口点在特化,typedef 和 enum 则可以当成是编译时的变量,它们都能在编译时递归式地依赖于别的模板,尤其是 enum, 甚至可以通过使用三元操作符(:?)实现编译时判断,而至于分支(if/else),它们的实现显然就在于特化了。

以上的说辞可能有些虚无,下面以 Loki 中光彩夺目的 typelist 为例简单展示一下模板都能做些什么。

typelist 是这样一个东西,你可以把它看成是一个链表,该链表中放的是类型,具体结构定义如下:

template <class T, class U> struct Typelist { typedef T Head; typedef U Tail; };

Head 是 typelist 中当前节点所保存的 type, Tail 是 typelist 中该节点之后的其它部分,可以看成是链表中的 next 指针,怎么判断哪个节点是 typelist 的最后一个节点呢?在链表中,next 为空的节点是最后一个节点,同理,在 typelist 中我们可以定义一个空的结点:

struct NullType { };

有了上如上的定义,于是我们可以如下这样子来使用 typelist 了:

typedef Typelist<char, Typelist<int, Typelist<short, NullType> >

但是上面的写法怎么看都很难用而且极端不美观,在这个看脸的时代这怎么行,所以 Loki 定义了一大堆宏来减轻使用者的负担,看这里,因为当时 c++ 的标准还不支持 variadic template parameter,这些宏事实上是相当死板恶心的,但这也是没法的事,不看它的实现就好了。

就这么一个简单的结构,现在我们要让它支持查找,定位,插入,删除等常规操作,是不是觉得有些为难甚至觉得不可能? 答案是这些操作都是可行的,比如说查找,现在给定一个如上定义的 typelist,怎么判断该 typelist 中是否存在某一个特定类型呢?

答案如下,其它的操作本质上差不多,有兴趣的读者可自行挑战一下,就不在这里啰嗦:

template<class TL, class T> struct IndexOf; // 当搜索空的 typelist 时,结果为 -1. template<> struct IndexOf<NullType, T> { enum { value = -1 }; }; // 当前节点的类型为所想要搜索的类型时 template<class T, class Tail> struct IndexOf<Typelist<T, Tail>, T> { enum { value = 0 }; }; // 当前节点不是所查找的类型时,递归地在 Tail 中进行查找。 template<class Head, class Tail, class T> struct IndexOf<Typelist<Head, Tail>, T> { enum { in_tail = IndexOf<Tail, T>::value }; // 使用三元操作符进行判断,T 是否在 Tail 中存在。 enum { value = (in_tail >= 0)? 1 + in_tail: -1 }; };

从上面的例子我们可以看到,因为 c++ 支持对模板递归式的解析(也就是一个模板依赖于另一个模板时,先解释被依赖的模板),使得模板事实上有了很强的编译时运行的能力,这种能力表面上看起来可能不容易操控,但却显然是潜力无限的,不过它的缺点也比较明显:

编译时代码与运行时代码搅在一起,在处理复杂问题时,程序的逻辑可能不容易读懂。

编译时调试现阶段的支持还不够好。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/a7fb40a1b6de2d5bee46cadbe1601778.html