【问题标题】:Problem with order of evaluation in parameter pack expansion参数包扩展中的评估顺序问题
【发布时间】:2021-12-09 12:01:13
【问题描述】:

我写了一个模板,它接受一个istream& 和一个函数,应该从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

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

如何修复此代码并强制对参数进行特定的评估顺序?也许在调用函数之前单独评估它们?在不违反标准和/或依赖特定实现或编译器的情况下是否有可能?

【问题讨论】:

    标签: c++ parameter-pack pack-expansion


    【解决方案1】:

    例如,我们可以使用中间的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]

    braced-init-listinitializer-list 中,initializer-clauses,包括由包扩展产生的任何内容,按照它们出现的顺序进行评估。也就是说,与给定 initializer-clause 相关联的每个值计算和副作用都在与在它后面的任何 initializer-clause 相关联的每个值计算和副作用之前进行排序initializer-list 的逗号分隔列表。 [ 注意:无论初始化的语义如何,这种评估顺序都成立;例如,当 initializer-list 的元素被解释为构造函数调用的参数时,它适用,即使调用的参数通常没有顺序约束。 ]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-05
      • 1970-01-01
      • 2011-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多