gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 22:08:30 30 4
gpt4 key购买 nike

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

它最近出现在 isocpp.org 上,没有任何解释。

最佳答案

简短的回答是“它做得不是很好”。

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

代码没有排序保证,如果返回值为f对于给定的 Arg有一个重载的 operator,它可能会产生不幸的副作用。

有一些空白:

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

我们将从内部开始。

f(std::forward<Args>(args))是一个不完整的陈述,可以用 ... 扩展。 .它将调用 fargs 之一上展开时。调用此声明INVOKE_F .

(INVOKE_F, 0)f(args) 的返回值, 适用 operator,然后 0 .如果返回值没有覆盖,这将丢弃 f(args) 的返回值并返回 0 .调用 INVOKE_F_0 .如果 f返回具有覆盖 operator,(int) 的类型, 坏事发生在这里,如果那个操作符返回一个非 POD-esque 类型,你可以在以后得到“有条件支持”的行为。

[](...){}创建一个将 C 风格的可变参数作为其唯一参数的 lambda。这与 C++11 参数包或 C++14 可变参数 lambda 不同。将非 POD-esque 类型传递给 ... 可能是非法的。功能。调用 HELPER

HELPER(INVOKE_F_0...)是参数包扩展。在调用 HELPER 的上下文中的operator() ,这是一个法律背景。参数的评估未指定,并且由于 HELPER 的签名INVOKE_F_0...可能应该只包含普通的旧数据(用 C++03 的说法),或者更具体地说 [expr.call]/p7 说:(通过 @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.

因此,这段代码的问题在于顺序未指定并且它依赖于行为良好的类型特定的编译器实现选择。

我们可以修复 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[]来自存在,但仍在评估 fargs...按顺序排列。

我的首选变体是:

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

它采用 nullary 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 的缺点技术是不是所有的编译器都喜欢它——扩展一个包含整个子语句的参数包不是他们期望必须做的事情。

关于c++ - 这个可变参数模板代码有什么作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28110699/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com