Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem with order of evaluation in parameter pack expansion

Tags:

I wrote a template that takes an istream& and a function and is supposed to extract all parameters for this function from the istream, call the function with these parameters and return the result. It all works fine, except for the order of evaluation of function parameters. See code, further details and final question below:

#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); } 

Input:

1 2 

Expected output:

input was 1 and 2 

Actual output:

input was 2 and 1 

I know that the evaluation of function parameters is implementation specific and obviously here the last parameter got evaluated first and read the first input.

How do I to fix this code and force a specific order of evaluation on the parameters? Maybe evaluate them seperately before calling the function? Is it even possible without violating the standard and/or relying on a specific implementation or compiler?

like image 241
churill Avatar asked Sep 21 '20 08:09

churill


People also ask

Does order of function parameters matter?

Yes, it matters. The arguments must be given in the order the function expects them.

What is the evaluation order of the function parameters in C++?

The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in a default argument, even if they are not evaluated. The same verbiage is used by C++14 standard as well, and is found under the same section.


1 Answers

For example, we can use an intermediate std::tuple to force the order of evaluation:

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)); } 

In contrast to function arguments, the order of evaluation of arguments in a braced list is fixed by their order in that list, [dcl.init.list/4]:

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions, are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. ]

like image 128
Evg Avatar answered Sep 20 '22 09:09

Evg