参数包扩展中评估顺序的问题

问题描述

我编写了一个模板,该模板带有一个istream一个函数,应该从#include <iostream> #include <vector> void Foo(int i,std::string s) { std::cout << "input was " << i << " and " << s << '\n'; } template<typename T> T Parse(std::istream &s) { T res; s >> res; return res; } template<typename TR,typename ... TArgs> TR Bar(std::istream &s,TR f(TArgs...) ) { return f(Parse<TArgs>(s)...); } int main() { Bar(std::cin,Foo); } 提取函数的所有参数,并使用这些参数调用函数并返回结果。除了评估功能参数的顺序外,一切都正常。请参见下面的代码,更多详细信息和最终问题:

1 2

输入:

input was 1 and 2

预期输出

input was 2 and 1

实际输出

AuthService

我知道功能参数的评估是特定于实现的,显然,这里最后一个参数首先被评估并读取第一个输入。

如何解决代码并在参数上强制执行特定的求值顺序?也许在调用函数之前分别评估它们?甚至有可能在不违反标准和/或不依赖特定实现或编译器的情况下进行

解决方法

例如,我们可以使用中间的std::tuple来强制求值顺序:

template<typename TR,typename ... TArgs>
TR Bar(std::istream &s,TR f(TArgs...) )
{
    std::tuple<TArgs...> args{Parse<TArgs>(s)...};
    return std::apply(f,std::move(args));
}

与函数参数相反,括号列表中参数的求值顺序由该列表[dcl.init.list/4]中的顺序确定:

括号初始化列表初始化器列表中,初始化器子句,包括由于包扩展而产生的所有子句,按照它们出现的顺序进行评估。就是说,与给定的 initializer-clause 相关的每个值计算和副作用都在其后的与任何 initializer-clause 相关的每个值计算和副作用之前进行排序。 initializer-list 的逗号分隔列表。 [注:无论初始化的语义如何,此评估顺序均成立;例如,它通常适用于将 initializer-list 的元素解释为构造函数调用的参数的情况,即使通常对该调用的参数没有排序约束。 ]