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?
Yes, it matters. The arguments must be given in the order the function expects them.
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.
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. ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With