注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

(转载)C++11及BOOST特性之十八变参模板  

2014-07-21 20:09:36|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

C++11BOOST特性之十八变参模板

 

 

(转载)C++ -- variadic template (可变参数模板)

 

作者:唐风

 

主页:http://www.cnblogs.com/liyiwen

 

C++11 语言核心的改进中,最为关注的有 rvalue reference (这里有一篇拙作),lambdavariadic templatervalue 规则稍微复杂,但一旦理解和记住了,应用上就没有什么困难。lambda 其实是一个“很自然”的语言设施,除了语法稍显诡异之外,习惯了就能马上用上,而且是能广泛用上的好东西。variadic template 这个新特性不像前两者,它本身的语法规则并不复杂,但是应用的时候确比较费脑子,好在这个技术主要是用在库的实现中,一个实现得好的库,并不会让我们对实现的细节作任何的要求。

 

variadic template 特性本身是一个很自然的需求,它完善了 C++ 的模板设计手段。原来的模板参数可以使类和函数的参数类型“任意化”,如果再加上“参数个数的任意化”,那么在参数方面的设计手段就基本上齐备了,有了variadic template 显然可以让设计出来的函数或是类有更大的复用性。因为有很多处理都是与“处理对象的个数”关系不大的,比如说打屏(printf),比如说比较大小(maxmin),比如函数绑定子(bindfunction要对应各种可能的函数就要能“任意”参数个数和类型)。如果不能对应任意个参数,那么就总会有人无法重用已有的实现,而不得不再重复地写一个自己需要的处理,而共通库的实现者为了尽可能地让自己写的类(函数)能复用在更多的场景,也不得不重复地写很多的代码或是用诡异的技巧,宏之类的去实现有限个“任意参数”的对应。(像TR1中的 bind 等)。

 

 

一、基本语法

声明一个带有可变参数个数的模板的语法如下所示:

 

template<typename Element> class tuple;

 

tuple<int, string> a;  // use it like this

在模板参数 Element 左边出现省略号 ... ,就是表示 Element 是一个模板参数包(template type parameter pack)。parameter pack(参数包)是新引入 C++ 中的概念,比如在这个例子中,Element 表示是一连串任意的参数打成的一个包。比如第2行中,Element 就是 int, string这个参数的合集。不仅“类型”的模板参数(也就是typename定义的参数)可以这样做,非类型的模板参数也可以这样做。比如下面这个例子:

 

template<typename T, unsigned PrimaryDimesion, unsigned.. Dimesions>

class array { /**/ };

 

array<double, 3, 3> rotation_matrix; //3x3 ratiation matrix

现在我们知道parameter pack了,怎么在程序中真正具体地去处理打包进来的“任意个数”的参数呢?我原来以为,编译器会提供一些像get_param<1>(Element) 之类的内建的“参数抽取函数”给程序员使用结果不是!!看来我的思路还是太“过程式了”。其实 C++11 用的是 unpack 和类似函数重载似的“模板特化”来抽取参数的。这是应用 variadic tempate 最“坑爹”的部分,因为它要求对“递归”和“人肉代码展开”有一定的功力啊。还是看例子吧:

 

template<typename... Elements> class tuple;

template<typename Head, typename... Tail>

class tuple<Head, Tail...> : private tuple<Tail...> {

    Head head;

public:

    /* implementation */

};

template<>

class tuple<> {

    /* zero-tuple implementation */

};

1行声明了一个可以对应任意参数的tuple类,第2行到7行声明了这个类的一个部分特化,注意,这就是抽取参数的典型方法了。给个图还是最方便用来理解的:

 

(如果看不全,点击它可以看全图)

 

好吧,我觉得这个图是本篇博客中最有价值的部分了,转载一定要注明出处啊!

 

只说明一下针对 parameter pack 相对的另一个概念,模板参数后面带省略号 ... 就是一个解包(unpack),会把这个参数所表示的参数列表解开后去匹配新的模板,或是进行模板展开。其它的,我觉得有上面这个图,实在不需要再多费什么笔墨去讲这一部分了。

 

二、例子

新的标准库里,有很多个库都直接依赖于 variadic template 这个语言特性,比如,tuplebindfunction。但他们比较复杂,也不够“惊艳”。C++ 老爸的 C++11 FQA Wikipedia 的例子都是“类型安全”的printf

 

void printf(const char *s)

{

    while (*s) {

        if (*s == '%') {

            if (*(s + 1) == '%') {

                ++s;

            }

            else {

                throw std::runtime_error("invalid format string: missing arguments");

            }

        }

        std::cout << *s++;

    }

}

 

template<typename T, typename... Args>

void printf(const char *s, T value, Args... args)

{

    while (*s) {

        if (*s == '%') {

            if (*(s + 1) == '%') {

                ++s;

            }

            else {

                std::cout << value;

                // call even when *s == 0 to detect extra arguments

                printf(s + 1, args...);

                return;

            }

        }

        std::cout << *s++;

    }

    throw std::logic_error("extra arguments provided to printf");

}

这个例子确实很棒,够短。仅仅X行的代码中,就用到了选择(if)和循环(while),递归(printf自身的递归),重载(cout<<对种输出类型的重载),异常。这个例子不细讲,大家试着用“上面的图”来试试分析一下吧。(这个例子用来面试,让面试者来解析下是不是不错?……)

 

三、杂想

这里有篇文章有一些关于这个特性加入 C++ 的动机,简而言之,像 function bind 之类如果没有这个特性,实现起来会非常的麻烦,而且这样实现出来的代码也会使得编译时间变得很长。

 

你看,如果要支持39个参数,编译时间会到700秒。而使用 variadic template 之后,代码小了 40 K,同时各种参数个数的情况下编译时间都小于 1 秒。

 

这很好。但是,这个特性用起来真的不是那么容易,你也看到了。在和其它的特性一起使用的时候(多重继承,右值引用)等在一起的时候,会更加地“复杂”。但它是一个利器,用得好,会造出非常好用的库。否则,我们就用库好了,不要为去学习,记住和非要在日常编程中使用它而烦恼了。

 

四、参考资料

Variadic Templates for GCChttp://www.generic-programming.org/~dgregor/cpp/variadic-templates.html

维基百科:http://en.wikipedia.org/wiki/Variadic_template

C++11当时的提案:N2080 (这个特别好,或是最好,我所见过的)

转载后的话:变参这个东西看起来是非常神秘的,其实你如果发现了他的原理就会觉得其实是很简单的。在这里,我们不得不佩服国外的这群BT的家伙们确实是牛啊

  评论这张
 
阅读(707)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017