编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异

题目挺绕口的。C++ 11的好东西不算太多,但变参模板(Variadic Template)肯定是其中耀眼的一颗明星,在C++设计新思维中,你可以看到很多模版的代码为了支持不确定的参数个数,而要重载1个参数到N个模板参数的N个函数。虽然种代码一般也是用会用宏和脚步辅助生成。但我想也没有人愿意看到几千行这种单调的函数。通过这个东东,模板的威力可以爆发。

目前的最新的编译器基本都已经支持Variadic Template了,GCC 4.6和Visual studio 2013都已经支持变参模板。但今天我在写一个Lua粘结层的时候发现了一个有意思的问题。

先上代码。

 #include <iostream>

 template <typename ... T>
void dummy_wrapper(T... t)
{
}; template <class T>
T unpacker(const T t)
{
std::cout << '[' << t << ']';
return t;
} template <typename... Args>
void write_line(const Args& ... data)
{
dummy_wrapper(unpacker(data)...);
std::cout << '\n';
} int main()
{
write_line(, "--", "2.2.2", "--", 3.0);
return ;
}

稍作解释,write_line是一个接受变参的模版函数,其内部调用dummy_wrapper,这个函数帮主辅助进行包扩展,让每个参数都调用unpacker(data)函数。那这个代码这个代码的输出预计应该是什么?

我想大部分人会认为是 [1][--][2.2.2][--][3],但其实在Visual C++ 2013和GCC 4.8上的输出都正好相反。输出的是[3][--][2.2.2][--][1]。是不是有点搞?

我觉得这个问题的原因是因为C++的参数入栈顺序是从右到左,1这个参数被计算处理后,被先扔到栈里面。反而变成了最右的参数。所以导致了这个问题。

其实坦白讲这种问题就是典型的规范没有写明的问题。其实我认为从结果和语义正确性上讲,[1][--][2.2.2][--][3]的输出肯定更加正确一些,但这个对于编译器,就要等若干结果计算出来了,再入栈。

而这个问题在IBM的编译器团队的写的《深入理解C++11》上也有一节提过,IBM的编译器XL编译器上类似的测试输出结果是顺序的,他们也测试了GCC,他们认为GCC输出是混乱的(其实是倒序的)。

如何规避这个问题呢?有个法子是把参数反着传递给write_line函数,但这种方法不具备可移植性(谁知道这个bug会哪天修复,另外,你输出东西的时候能倒着想问题?)。

其实可以考虑使用递归的包扩展方式,

 #include <iostream>

 template <typename ... Tlist>
void dummy_wrapper(Tlist... tlst)
{
}; template <typename T, typename ... Tlist>
void dummy_wrapper(T t, Tlist... tlst)
{
unpacker(t);
dummy_wrapper(tlst...);
}; template <class T>
T unpacker(const T t)
{
std::cout << '[' << t << ']';
return t;
} template <typename... Args>
void write_line(const Args& ... data)
{
dummy_wrapper(data...);
std::cout << '\n';
} int main()
{
write_line(, "--", "2.2.2", "--", 3.0);
return ;
}

这样改写后,输出变成[1][--][2.2.2][--][3]、

这种方式参数是一个个展开的,所以避免了上述的问题。大部分情况可以这样改写规避一部分问题。但当然这样也不能解决所有问题,

我的代码里面中间有一段是这样的,目的是为了将一个函数注册给lua,

 template<typename ret_type,
typename ...args_type>
class g_functor_ret
{
public:
static int invoke(lua_State *state)
{
void *upvalue_1 = lua_touserdata(state, lua_upvalueindex());
ret_type(*fun_ptr)(args_type...) = (ret_type( *)(args_type...))(upvalue_1);
int para_idx = ;
push_stack<ret_type>(state, fun_ptr(read_stack<args_type>(state, para_idx++)...));
}
};

fun_ptr(read_stack<args_type>(state, para_idx--)...) 这段代码,由于fun_ptr是一个函数指针,我不可能为所有的函数指针都去写一个递归方法,所以上面的方法无效。

怎么办呢?只能先把参数反向传入,后面等编译器有更新再慢慢改,也只有这个很挫的方法先应付。目前我能测试到的VS2013和GCC 4.8目前还都有问题。

【本文作者是雁渡寒潭,本着自由的精神,你可以在无盈利的情况完整转载此文档,转载时请附上BLOG链接:http://www.cnblogs.com/fullsail/,否则每字一元,每图一百不讲价。对Baidu文库和360doc加价一倍】

编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异的更多相关文章

  1. C++11新特性 变参模板、完美转发(简述)

    变参模板 (Variadic Template) - 使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建 完美转发 - 使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另 ...

  2. C++11 : 外部模板(Extern Template)

    在C++98/03语言标准中,对于源代码中出现的每一处模板实例化,编译器都需要去做实例化的工作:而在链接时,链接器还需要移除重复的实例化代码.显然,让编译器每次都去进行重复的实例化工作显然是不必要的, ...

  3. 模板(Template)

    最近阅读google chromium base container stack_container代码,深刻感觉到基础知识不扎实. // Casts the buffer in its right ...

  4. 【C/C++开发】C++11的模板类型判断——std::is_same和std::decay

    C++11的模板类型判断--std::is_same和std::decay 问题提出:有一个模板函数,函数在处理int型和double型时需要进行特殊的处理,那么怎么在编译期知道传入的参数的数据类型是 ...

  5. c++11-17 模板核心知识(十二)—— 模板的模板参数 Template Template Parameters

    概念 举例 模板的模板参数的参数匹配 Template Template Argument Matching 解决办法一 解决办法二 概念 一个模板的参数是模板类型. 举例 在c++11-17 模板核 ...

  6. 重载(Overloading)以及模板(Template)

    继续<C++ premier plus>的学习 (1)函数重载,通俗来说,就是相同的函数名字名下,存在多个函数,要使得这成立,各个同名函数必须形参列表(也称为"签名", ...

  7. Django——模板层(template)(模板语法、自定义模板过滤器及标签、模板继承)

    前言:当我们想在页面上给客户端返回一个当前时间,一些初学者可能会很自然的想到用占位符,字符串拼接来达到我们想要的效果,但是这样做会有一个问题,HTML被直接硬编码在 Python代码之中. 1 2 3 ...

  8. Elasticsearch之索引模板index template与索引别名index alias

    为什么需要索引模板? 在实际工作中针对一批大量数据存储的时候需要使用多个索引库,如果手工指定每个索引库的配置信息(settings和mappings)的话就很麻烦了. 所以,这个时候,就存在创建索引模 ...

  9. DjangoMTV模型之视图层views及模板层template

    Django视图层中views的内容 一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容(render),也可以是一个重定向( ...

随机推荐

  1. Linux下安装、配置、启动Apache

    http://www.cnblogs.com/zhuque/archive/2012/11/03/2763352.html#

  2. Struts2 Package

    package 元素的所有属性及对应功能: Attribute Required Description name yes key to for other packages to reference ...

  3. sqort函数用法总结

    qsort包含在<stdlib.h>头文件中,此函数根据你给的比较条件进行快速排序,通过指针移动实现排序.排序之后的结果仍然放在原数组中.使用qsort函数必须自己写一个比较函数. 函数原 ...

  4. StaggeredGridLayoutManager

    Class Overview A LayoutManager that lays out children in a staggered grid formation. It supports hor ...

  5. html5 touch事件实现触屏页面上下滑动(一)

    最近做的做那个app的项目由于用overflow:hidden导致了很多问题,于是决定研究下html5的touch事件.想找个全面点的帖子真是难死了,虽然好多关于html5 touch的文章但大多都是 ...

  6. 函数xdes_calc_descriptor_page

    根据偏移量计算出第几个xdes page 0 %16328 = 0 64% 16328 = 64 128 % 16328 = 128 192 % 16328 = 192 /************** ...

  7. UVa 1401 (Tire树) Remember the Word

    d(i)表示从i开始的后缀即S[i, L-1]的分解方法数,字符串为S[0, L-1] 则有d(i) = sum{ d(i+len(x)) | 单词x是S[i, L-1]的前缀 } 递推边界为d(L) ...

  8. UVa 11722 (概率 数形结合) Joining with Friend

    高中也做个这种类似的题目,概率空间是[t1, t2] × [s1, s2]的矩形,设x.y分别代表两辆列车到达的时间,则两人相遇的条件就是|x - y| <= w 从图形上看就是矩形夹在两条平行 ...

  9. UVa 12304 (6个二维几何问题合集) 2D Geometry 110 in 1!

    这个题能1A纯属运气,要是WA掉,可真不知道该怎么去调了. 题意: 这是完全独立的6个子问题.代码中是根据字符串的长度来区分问题编号的. 给出三角形三点坐标,求外接圆圆心和半径. 给出三角形三点坐标, ...

  10. Jqgrid入门-Jqgrid列数据拖动(七)

    上一章提到在Jqgrid中如何设置二级表头,这一章节主要探讨Jqgrid表格里面的数据如果实现拖动功能,比如你想把第一行的数据拖到当前页的最后一行,或者其他位置.     Jqgrid表格插件自己没有 ...