趁着国庆长假快速翻了一遍传说中的、大名鼎鼎的 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++ 支持对模板递归式的解析(也就是一个模板依赖于另一个模板时,先解释被依赖的模板),尤其是 template template parameter,使得模板事实上有了很强的编译时运行的能力,这种能力表面上看起来可能不容易操控,但却显然是潜力无穷的,不过它的缺点也比较明显:

  1. 编译时代码与运行时代码搅在一起,在处理复杂问题时,程序的逻辑可能不容易读懂。
  2. 编译时调试现阶段的支持还不够好,错误信息异常地冗余而且不精确。

网络上关于 c++ 模板元编程的讨论有很多,模板的各种能力技巧也渐渐被越来越多的人所发现所挖掘,但是在实际的工作中,对很多人来说模板元编程却仍一直处于比较保守的阶段,说到底过分依赖模板元编程所带来的缺点还是太明显,就我粗陋的见闻来说,完全基于模板元编程做出来的比较出名的工具型的东西,主要有几个(都来自 boost):boost spirit,boost proto 及 boost mpl,东西都非常非常棒,但使用的体验嘛,老实说都不是很好。。。特别是 spirit。而至于它们的实现,对有兴趣练习深入这方面技能的程序猿来说,这两者倒确实是不可多得好素材,尤其是 proto, 代表了一个高峰。

好消息是,伴随着 c++11 的到来,好些众人期盼以久的新特性终于从理想照进现实,尤其是 variadic parameter 的加入,可以预见将再度大大提升模板的能力,c++ 标准沉寂近10年后迎来了一个新时期,甚至还有人曾经提议要加入 static_if,concept 等概念也在酝酿中了,变化是唯一永恒不变的东西,你,作好准备了吗?

c++ 模板元编程的一点体会的更多相关文章

  1. C++ 模板元编程 学习笔记

    https://blog.csdn.net/K346K346/article/details/82748163 https://www.jianshu.com/p/b56d59f77d53 https ...

  2. C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE

    本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的 ...

  3. C++模板元编程(C++ template metaprogramming)

    实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...

  4. C++模板元编程 - 2 模仿haskell的列表以及相关操作

    这是昨天和今天写的东西,利用C++的可变模板参数包以及包展开,模式匹配的一些东西做的,感觉用typename...比轮子叔那个List<A,List<B, List<C, D> ...

  5. 现代c++与模板元编程

    最近在重温<c++程序设计新思维>这本经典著作,感慨颇多.由于成书较早,书中很多元编程的例子使用c++98实现的.而如今c++20即将带着concept,Ranges等新特性一同到来,不得 ...

  6. 现代c++模板元编程:遍历tuple

    tuple是c++11新增的数据结构,通过tuple我们可以方便地把各种不同类型的数据组合在一起.有了这样的数据结构我们就可以轻松模拟多值返回等技巧了. tuple和其他的容器不同,标准库没有提供适用 ...

  7. C++模板元编程 - 函数重载决议选择工具(不知道起什么好名)完成

    这个还是基于之前实现的那个MultiState,为了实现三种类型“大类”的函数重载决议:所有整数.所有浮点数.字符串,分别将这三种“大类”的数据分配到对应的Converter上. 为此实现了一些方便的 ...

  8. C++模板元编程 - 挖新坑的时候探索到了模板元编程的新玩法

    C++真是一门自由的语言,虽然糖没有C#那么多,但是你想要怎么写,想要实现什么,想要用某种编程范式或者语言特性,它都会提供. 开大数运算类的新坑的时候(又是坑),无意中需要解决一个需求:大数类需要分别 ...

  9. 读书笔记_Effective_C++_条款四十八:了解模板元编程

    作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...

随机推荐

  1. html中的图片直接使用字符串代替

    最近来了一个网页,里面有图片,但是却没有引用外部的图片资源,很好奇.查看代码后发现,里面的图片是使用base64编码后的字符串代替了,这个叫做Data URI scheme. Data URI sch ...

  2. duilib进阶教程 -- 设置资源路径 (15)

    在前面的教程里,虽然图片都放到了skin文件夹里,但是XML却都在外面,当XML比较多时,就不太好看啦,如下图: 所以需要整理一下,将XML也放入skin文件夹,这样exe的目录就简洁多了: 将XML ...

  3. paip.关于动画特效原理 html js 框架总结

    paip.关于动画特效原理 html js 框架总结 1. 动画框架的来源:flex,jqueryui 3 2. 特效的分类 3 2.1. Property effects 动态改变一个或多个目标对象 ...

  4. TF Boys (TensorFlow Boys ) 养成记(二)

    TensorFlow 的 How-Tos,讲解了这么几点: 1. 变量:创建,初始化,保存,加载,共享: 2. TensorFlow 的可视化学习,(r0.12版本后,加入了Embedding Vis ...

  5. 在ArcGIS空间数据库中增加点数据的方法

    1.新建一个mxd(ArcMAP)文件 2.从ArcCatalog中把要编辑的图层拖到ArcMAP中 3.从ArcCatalog中拖一个参照图层到ArcMAP中,比如临沂市的县级区划图 4.打开Edi ...

  6. 12套swift学习资源分享

    虽然objective-c编程语言在过去很长一段时间都是iOS应用开发的基础语言,且很多iOS开发者对其也深爱有佳,但是随着swift编程语言的问世,迅速发展为开发者追捧的语言.且今年伴随着swift ...

  7. [推荐]DDOS攻击与防范知识介绍

    [推荐]DDOS攻击与防范知识介绍 DDOS攻防体系建设v0.2(淘宝-林晓曦)     http://wenku.baidu.com/view/39549a11a8114431b90dd866.ht ...

  8. easy datagrid 按钮控制

    onBeforeLoad : function() {// 这里是紧接着你的修改按钮的 // 注意ID为你初始化工具栏按钮对应的ID var adminid=<%=Admin_Id%>+' ...

  9. nginx内置全局变量及含义

    名称        版本        说明(变量列表来源于文件 ngx_http_variables ) $args        1.0.8        请求中的参数; $binary_remo ...

  10. arcgis server10.1注册服务——避免在发布服务中拷贝数据

    之前用的arcgis10.1前的版本,后来换成10.1还有点不习惯,变化挺多的.发布服务过程中,进行分析的时候,其中有一项提醒:xxx图层没有注册到服务,很纳闷,为什么会有这种提示,不管,点击发布,会 ...