将可变参数模板的参数包含在初始化程序列表中应该确保它们按顺序进行评估,但不会在此处进行:
#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;
}
怎么来的?
这似乎是一个 窃听器。输出应该是您期望的输出。
虽然不能保证构造函数调用的参数的评估顺序 一般来说,对支撑初始化列表中表达式的评估顺序有保证。
根据C ++ 11标准的第8.5.4 / 4段:
在braced-init-list的initializer-list中,initializer-clause,包括pack中的任何结果
扩展(14.5.3),按它们出现的顺序进行评估。也就是说,每个值计算和
在每个值计算和侧面之前,对与给定初始化子句相关联的副作用进行排序
与在初始化列表的逗号分隔列表中跟随它的任何initializer子句相关联的效果。
[注意:无论初始化的语义如何,此评估顺序都保持不变;例如, 它适用
当initializer-list的元素被解释为构造函数调用的参数时,即使如此
通常,对呼叫的参数没有排序限制。 - 尾注]
如上所述,您的问题是编译器错误。您编写的代码应按顺序评估其参数。
我的建议是明确你想做什么,你做了什么顺序,并避免滥用逗号运算符(顺便说一下,如果你的代码可能会表现得很奇怪,如果 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中包装你想要做的动作,然后使用 ...
创建一整套所述lambda的实例并传递给 do_in_order
,通过完美转发按顺序调用它们。
这对于编译器来说应该很容易内联并减少到一系列调用。它直接表示它的作用,并不需要奇怪的空转,使用逗号运算符或其值被丢弃的数组。