Fold expressions seem to be a nice way to apply a function to each element of a tuple. However, if the applied function has side effects, the order of function invocations might be an important concern.
Consider:
#include <iostream>
template<typename... Ts>
void printStuff(Ts... args)
{
( ([](auto&& v) { std::cout << v << " "; })(args), ... );
std::cout << '\n';
}
int main()
{
printStuff("hello", 42, 1.5f);
// expected output: hello 42 1.5
}
This seems to work.
But is the order of evaluation for the lambdas guaranteed here or could I end up with the values being flipped around in the output? Does the answer change if I used a different operator for chaining the commands together?
A right-fold over an operator expands like this:
... (arg0 op (arg1 op arg2))
. So while the parens help, they don't guarantee anything about the order of the individual elements.Therefore, it's all left up to
op
. And the comma operator (which is distinct from commas separating function arguments), even pre-C++17, is a hard sequence point. It ensures left-to-right evaluation with no cross-talk.If you had instead used
+
, there would be no sequencing guarantees. So it depends on the operator you use. C++17 added a few more operators that have strict sequencing guarantees (<<
, for example).