简体   繁体   English

使用可变参数lambda迭代可变参数函数模板的参数

[英]Iterating through parameters of a variadic function template using variadic lambda

Suppose we have the following function template: 假设我们有以下函数模板:

template <typename Functor, typename... Arguments>
void IterateThrough(Functor functor, Arguments&&... arguments)
{
    // apply functor to all arguments
}

This function is usually implemented as follows: 此功能通常如下实现:

template <typename Functor, typename... Arguments>
void IterateThrough1(Functor functor, Arguments&&... arguments)
{
    int iterate[]{0, (functor(std::forward<Arguments>(arguments)), void(), 0)...};
    static_cast<void>(iterate);
}

Another way: 其他方式:

struct Iterate
{
    template <typename... Arguments>
    Iterate(Arguments&&... arguments)
    {
    }
};

template <typename Functor, typename... Arguments>
void IterateThrough2(Functor functor, Arguments&&... arguments)
{
    Iterate{(functor(std::forward<Arguments>(arguments)), void(), 0)...};
}

I have found yet another approach which uses a variadic lambda: 我找到了另一种使用可变参数lambda的方法:

template <typename Functor, typename... Arguments>
void IterateThrough3(Functor functor, Arguments&&... arguments)
{
    [](...){}((functor(std::forward<Arguments>(arguments)), void(), 0)...);
}

What pros and cons has this method in comparison with first two? 与前两个相比,这种方法有哪些优点和缺点?

The calls to functor are now unsequenced. functor的调用现在还没有排序。 The compiler can call functor with your expanded arguments in any order it wants. 编译器可以按照所需的任何顺序使用扩展的参数调用functor As an example, IterateThrough3(functor, 1, 2) could do functor(1); functor(2); 例如, IterateThrough3(functor, 1, 2)可以做IterateThrough3(functor, 1, 2) functor(1); functor(2); functor(1); functor(2); or it could do functor(2); functor(1); 或者它可以做functor(2); functor(1); functor(2); functor(1); , whereas the other two always do functor(1); functor(2); 而另外两个总是做functor(1); functor(2); functor(1); functor(2); .

Section 8.5.4/4 of the standard requires that any expressions inside a {} initialiser are evaluated left-to-right. 该标准的第8.5.4 / 4节要求{}初始化程序内的任何表达式都是从左到右进行计算。

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear. 在braced-init-list的initializer-list中,initializer-clauses(包括pack扩展(14.5.3)产生的任何结果)按照它们出现的顺序进行评估。

Section 5.2.2/4 states that arguments to a function call can be evaluated in any order. 第5.2.2 / 4节规定可以按任何顺序评估函数调用的参数。

When a function is called, each parameter (8.3.5) shall be initialized (8.5, 12.8, 12.1) with its corresponding argument. 调用函数时,应使用相应的参数初始化每个参数(8.3.5)(8.5,12.8,12.1)。 [Note: Such initializations are indeterminately sequenced with respect to each other (1.9) — end note ] [注意:这种初始化相对于彼此不确定地排序(1.9) - 结束注释]

This might not cover the wording of the order of evaluation (which I can't find ATM), but it is well known that arguments to functions are evaluated in an unspecified order. 这可能不包括评估顺序的措辞(我找不到ATM),但众所周知,函数的参数是以未指定的顺序进行评估的。 EDIT: see @dyp's comment for a relevant standard quote. 编辑:请参阅@ dyp对相关标准报价的评论。

When you use the variadic lambda, the order of evaluation of arguments is unspecified as per the language specification (which in turn means the evaluation of functor(argument) could be in any order unknown to the programmer). 当你使用variadic lambda时,参数的评估顺序是根据语言规范而未指定的(这反过来意味着functor(argument)的评估可以是程序员不知道的任何顺序)。 That is the only difference. 这是唯一的区别。 You can search "order of evaluation of arguments" on this site, you will see many topics on it. 您可以在此站点上搜索“参数评估顺序”,您将看到许多主题。

As for the variadic templated constructor approach, that should work as long as you use list-initialization to invoke it, otherwise it would have the same problem as the lambda. 至于可变参数模板化构造方法,只要你使用list-initialization来调用它就应该工作,否则它会和lambda有同样的问题。 Note that GCC (upto 4.8.2) has bug, so this doesn't work, though I don't have any idea whether it is fixed with the recent version of GCC. 请注意,GCC(高达4.8.2)有错误,所以这不起作用,但我不知道它是否与最新版本的GCC一起修复。

If your goal is to have a one-liner with no external machinery, you can use a lambda that accepts std::initializer_list : 如果您的目标是使用没有外部机器的单线程,则可以使用接受std::initializer_list的lambda:

template <typename Functor, typename... Arguments>
void IterateThrough3(Functor functor, Arguments&&... arguments)
{
    [](std::initializer_list<int>){}(
        {((void)functor(std::forward<Arguments>(arguments)), 0)...}
    );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM