可变参数模板参数上的C++11初始值设定项列表:为什么这不起作用

C++11 initializer lists on variadic template's parameters: why isn't this working

本文关键字:参数 列表 为什么 不起作用 变参 C++11      更新时间:2023-10-16

将可变模板的参数封装在初始值设定项列表中可以确保它们按顺序求值,但在这里不会发生:

#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段:

在支撑的初始化列表的初始化器列表中,初始化器子句,包括来自pack的任何结果展开式(14.5.3(按它们出现的顺序进行评估。也就是说,每个值的计算和与给定初始值设定项子句相关的副作用在每次值计算和副作用之前进行排序在初始值设定项列表的逗号分隔列表中,与其后的任何初始值设定项子句关联的效果。[注意:无论初始化的语义如何,此求值顺序都适用;例如,它适用于当初始值设定项列表的元素被解释为构造函数调用的参数时,即使通常,调用的参数没有顺序约束--尾注]

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

我的建议是明确你想做什么,以及按照什么顺序做,避免滥用逗号运算符(顺便说一句,如果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)); }... );

将要执行的操作封装在一个匿名的null完全捕获lambda中,然后使用...创建所述lambda的一整套实例,并传递给do_in_order,后者通过完美转发按顺序调用它们。

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