为什么此参数包中的函数调用要向后求值?

itz*_*ode 1 c++ for-loop template-meta-programming

最近我发现了这个关于使用模板展开循环的StackOverflow 答案。答案指出“这个想法适用于 C++11”,我最终得到了这个:

namespace tmpl {
  namespace details {
    template<class T, T... values>
    class integer_sequence {
    public:
      static constexpr size_t size() { return sizeof...(values); }
    };
    
    template<class T, class N, class... Is>
    struct make_integer_sequence_helper :
      make_integer_sequence_helper<T, std::integral_constant<T, N::value - 1>, std::integral_constant<T, N::value - 1>, Is...> {};
      
    template<class T, class... Is>
    struct make_integer_sequence_helper<T, std::integral_constant<T, 0>, Is...> {
      using type = integer_sequence<T, Is::value...>;
    };
    
    template<class T, T N>
    using make_integer_sequence = typename make_integer_sequence_helper<T, std::integral_constant<T, N>>::type;
    
    template<class... Ts>
    void variadic_noop(Ts... params) {}
    
    template<class F, class T>
    int call_and_return_0(F&& f, T i) {f(i); return 0;}
    
    template<class T, T... Is, class F>
    void loop(integer_sequence<T, Is...>, F&& f) {
      variadic_noop(call_and_return_0(f, Is)...);
    }
  }
  template<class T, T max, class F>
  void loop(F&& f) {
    details::loop(details::make_integer_sequence<T, max>{}, f);
  }
}
Run Code Online (Sandbox Code Playgroud)

让我们举一个简单的例子来说明如何使用该模板:

tmpl::loop<size_t, 20>([&](size_t idx) {
  cout << "Loop " << idx << std::endl;
});
Run Code Online (Sandbox Code Playgroud)

当我使用其他答案中的 C++17 代码时,它从 0 迭代到 19。但是,我编写的 C++11 jank 从 19 迭代到 0。

理论上,当details::loop()展开时它应该变成这样:

variadic_noop(call_and_return_0(f, 0), call_and_return_0(f, 1), call_and_return_0(f, 2), ...);
Run Code Online (Sandbox Code Playgroud)

call_and_return_0(f, 19)那么,如果它是最后一个参数,为什么 C++ 首先运行呢variadic_noop()

Bri*_*ian 5

函数调用中函数参数的求值可以是从左到右、从右到左或任何其他顺序,这不需要是可预测的。

但是,当您有一个子表达式 , , ..., 由逗号运算符分隔的单个表达式e_1, e_2, ..., e_ne_1e_2表达式e_n将始终按从左到右的顺序计算,因为逗号运算符保证其左操作数在其右操作数之前求值。(在 C++17 之前,此保证仅适用于内置逗号运算符,不适用于任何重载运算符。)

尽管函数调用也使用逗号,但函数调用中的逗号不强制从左到右求值。链接答案中的代码使用 C++17 折叠表达式创建一系列由逗号运算符分隔的表达式。