c – 这个可变模板代码做了什么? - 代码日志

c – 这个可变模板代码做了什么?

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

最近在isocpp.org上没有解释。

简短的答案是“它不是很好”。

它在每个args …上调用f,并舍弃返回值。但是,这样做在某种情况下会导致意想不到的行为,这是不必要的。

代码没有排序保证,如果给定的Arg的返回值具有重载操作符,则可能会产生不幸的副作用。

有一些空白:

[](...){}(
  (
    f(std::forward<Args>(args)), 0
  )...
);

我们将从里面开始。

f(std :: forward< Args>(args))是一个不完整的语句,可以用….扩展。它会在扩展时调用其中一个参数。调用这个语句INVOKE_F。

(INVOKE_F,0)获取f(args)的返回值,应用运算符,然后为0.如果返回值没有覆盖,则丢弃f(args)的返回值并返回0.调用此INVOKE_F_0。如果f返回一个带有overriden运算符(int)的类型,那么这里会发生坏事情,如果该运算符返回非POD类型,那么稍后可以获得“有条件支持”的行为。

[](…){}创建一个使用C风格变量作为唯一参数的lambda。这与C 11参数包或C 14变体羊羔不同。将非POD类型类型传递给…函数可能是非法的。打电话给这个帮助

HELPER(INVOKE_F_0 …)是参数包扩展。在调用HELPER的operator()的上下文中,这是一个合法的上下文。参数的评估是未指定的,并且由于HELPER INVOKE_F_0 …的签名可能应该只包含普通的旧数据(以C 03的说法),或者更具体地说[expr.call] / p7说:(via @ T.C)

Passing a potentially-evaluated argument of class type (Clause 9) having a nontrivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.

所以这个代码的问题是这个顺序是未指定的,它依赖于良好的行为类型或特定的编译器实现选择。

我们可以修复运算符,问题如下:

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
}

并且没有什么好的理由,编译器不能从存在中消除未使用的[],同时依次对args …进行评估。

我的首选变体是:

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

这是无效的lambdas,一次运行,从左到右。 (如果编译器可以证明顺序并不重要,那么可以自由地将它们按顺序运行)。

我们可以用以下方式实现上述:

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清除:但是,除非极端的原因,使参数包扩展中的代码以可预测的顺序运行可以减少惊喜并使头痛最小化。

do_in_order技术的缺点在于并不是所有的编译器都喜欢它 – 扩展包含整个子语句的包含语句的语句不是他们期望做的事情。

http://stackoverflow.com/questions/28110699/what-does-this-variadic-template-code-do

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c – 这个可变模板代码做了什么?