11

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

#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;
}

怎么来的?

4

2 回答 2

13

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

虽然通常不能保证构造函数调用的参数的求值顺序但可以保证括号初始化器列表中表达式的求值顺序。

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

在花括号初始化列表的初始化列表中,初始化子句,包括由包扩展 (14.5.3) 产生的任何初始化子句,按照它们出现的顺序进行评估。也就是说,与给定初始化子句相关联的每个值计算和副作用在初始化器列表的逗号分隔列表中与任何初始化子句相关联的每个值计算和副作用之前进行排序。[注意:无论初始化的语义如何,这种评估顺序都成立;例如,当初始化列表的元素被解释为构造函数调用的参数时,它适用,即使调用的参数通常没有顺序约束。——尾注]

于 2013-04-15T14:39:44.923 回答
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,它通过完美转发按顺序调用它们。

这对于编译器来说应该很容易内联并简化为一系列调用。它直接说明了它的作用,不需要奇怪的 void 强制转换、逗号运算符的使用或值被丢弃的数组。

于 2013-04-15T14:38:57.073 回答