C ++ 11初始化器列出了可变参数模板的参数:为什么这不起作用

时间:2013-04-15 14:29:40

标签: c++ templates c++11 variadic-templates

将可变参数模板的参数包含在初始化列表中应确保按顺序对它们进行评估,但不会在此处进行评估:

#include <iostream>
using namespace std;


template<class T> void some_function(T var)
{
   cout << var << endl;
}

struct expand_aux {
    template<typename... Args> expand_aux(Args&&...) { }
};

template<typename... Args> inline void expand(Args&&... args) 
{
   bool b[] = {(some_function(std::forward<Args>(args)),true)...}; // This output is 42, "true", false and is correct
   cout << "other output" << endl;
   expand_aux  temp3 { (some_function(std::forward<Args>(args)),true)...  }; // This output isn't correct, it is false, "true", 42
}

int main()
{
   expand(42, "true", false);

   return 0;
}

怎么回事?

2 个答案:

答案 0 :(得分:13)

这似乎是错误。输出应该是您期望的输出。

虽然不能保证构造函数调用的参数的评估顺序,但是对支撑的初始化列表中的表达式的评估顺序有保证。

根据C ++ 11标准的第8.5.4 / 4段:

  

在braced-init-list的initializer-list中,初始化子句,包括包中的任何结果   扩展(14.5.3)按照它们出现的顺序进行评估。也就是说,每个值计算和   在每个值计算和侧面之前,对与给定初始化子句相关联的副作用进行排序   与在初始化列表的逗号分隔列表中跟随它的任何initializer子句相关联的效果。   [注意:无论初始化的语义如何,此评估顺序都保持不变;例如,适用   当initializer-list的元素被解释为构造函数调用的参数时,即使如此   通常,对呼叫的参数没有排序限制。 - 后注]

答案 1 :(得分:4)

如上所述,您的问题是编译器错误。您编写的代码应按顺序评估其参数。

我的建议是明确你想做什么,以及你在做什么顺序,并避免滥用逗号操作符(除此之外,如果some_function返回一个代码,你的代码会表现得很奇怪覆盖operator,)或使用初始化列表保证的类型(虽然标准,但也相对模糊)。

我的解决方案是编写然后使用do_in_order

// do nothing in order means do nothing:
void do_in_order() {}
// do the first passed in nullary object, then the rest, in order:
template<typename F0, typename... Fs>
void do_in_order(F0&& f0, Fs&&... fs) {
  std::forward<F0>(f0)();
  do_in_order( std::forward<Fs>(fs)... );
}
你使用的

do_in_order( [&]{ some_function(std::forward<Args>(args)); }... );

你在匿名的nullary full-capture lambda中包装你想要做的动作,然后使用...创建一组所述lambdas的实例并传递给do_in_order,调用它们通过完美转发订购。

这对于编译器来说应该很容易内联并减少到一系列调用。并且它直接表示它的作用,并且不需要奇怪的空转,使用逗号运算符或其值被丢弃的数组。