这个可变参数模板代码有什么作用?

时间:2015-01-23 13:12:09

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

template <class F, class... Args> 
void for_each_argument(F f, Args&&... args) { 
    [](...){}((f(std::forward<Args>(args)), 0)...); 
}

最近在isocpp.org上发布了没有解释的内容。

2 个答案:

答案 0 :(得分:25)

简短的回答是&#34;它做得不太好&#34;。

它会在每个f上调用args...,并丢弃返回值。但它确实会以不必要的方式在许多情况下导致意外行为。

代码没有排序保证,如果给定f的{​​{1}}的返回值过载Arg,则会产生不幸的副作用。

有一些空格:

operator,

我们将从内部开始。

[](...){}( ( f(std::forward<Args>(args)), 0 )... ); 是一个不完整的陈述,可以使用f(std::forward<Args>(args))进行扩展。展开后,它会在...之一上调用f。请将此声明称为args

INVOKE_F获取(INVOKE_F, 0)的返回值,应用f(args)然后operator,。如果返回值没有覆盖,则会丢弃0的返回值并返回f(args)。请拨打此0。如果INVOKE_F_0返回带有覆盖f的类型,则会发生错误,如果该运算符返回非POD类型,则可以获得&#34;有条件支持&#34;以后的行为。

operator,(int)创建一个lambda,它将C风格的可变参数作为唯一的参数。这与C ++ 11参数包或C ++ 14可变参数lambda不同。将非POD-esque类型传递给[](...){}函数可能是违法的。请拨打此...

HELPER是参数包扩展。在调用HELPER(INVOKE_F_0...) HELPER的情况下,这是一个法律背景。对参数的评估是未指定的,并且由于operator() HELPER的签名可能只应包含普通旧数据(用C ++ 03用语),或者更具体地说[expr.call] / p7表示:(通过@TC)

  

通过实现定义的语义有条件地支持传递具有非平凡复制构造函数,非平凡移动构造函数或非平凡析构函数的类类型(第9章)的可能已评估的参数,其中没有相应的参数。

因此,此代码的问题是订单未指定它依赖于行为良好的类型特定的编译器实现选择。

我们可以按如下方式解决INVOKE_F_0...问题:

operator,

然后我们可以通过在初始化程序中扩展来保证订单:

template <class F, class... Args> 
void for_each_argument(F f, Args&&... args) { 
  [](...){}((void(f(std::forward<Args>(args))), 0)...); 
}

但是当template <class F, class... Args> void for_each_argument(F f, Args&&... args) { int unused[] = {(void(f(std::forward<Args>(args))), 0)...}; void(unused); // suppresses warnings } 为空时上述操作失败,请添加另一个Args...

0

并且没有充分的理由让编译器不会从存在中消除template <class F, class... Args> void for_each_argument(F f, Args&&... args) { int unused[] = {0, (void(f(std::forward<Args>(args))), 0)...}; void(unused); // suppresses warnings } ,同时仍按顺序评估unused[]上的f

我的首选变体是:

args...

采用nullary lambdas并从左到右一次运行它们。 (如果编译器可以证明顺序无关紧要,那么可以自由地按顺序运行它们。)

然后我们可以用以下方式实现上述内容:

template <class...F>
void do_in_order(F&&... f) { 
  int unused[] = {0, (void(std::forward<F>(f)()), 0)...}; 
  void(unused); // suppresses warnings
}

这使得&#34;奇怪的扩张&#34;在一个孤立的函数(template <class F, class... Args> void for_each_argument(F f, Args&&... args) { do_in_order( [&]{ f(std::forward<Args>(args)); }... ); } )中,我们可以在其他地方使用它。我们也可以编写类似的do_in_order,但使do_in_any_order清楚:但是,除了极端的原因,在参数包扩展中以可预测的顺序运行代码可以减少意外并将头痛降到最低。

any_order技术的缺点是并非所有编译器都喜欢它 - 扩展包含包含整个子语句的语句的参数包并不是他们期望必须做的事情。

答案 1 :(得分:13)

实际上,它以f中的每个参数以未指定的顺序调用函数args

[](...){}

创建lambda函数,它不执行任何操作并接收任意数量的参数(va args)。

((f(std::forward<Args>(args)), 0)...)

lambda的参数。

(f(std::forward<Args>(args)), 0)

使用转发的参数调用f,将0发送给lambda。

如果您想要指定订单,可以使用following thing

using swallow = int[];
(void)swallow{0, (f(std::forward<Args>(args)), 0)...};