As you've seen, the standard does not define an ordering here. I've only seen it happen in reverse order, but in principle a compiler could do anything it wanted. Worse, your request for a "standardized constructor" will not prosper, because this issue isn't specific to constructors: all function arguments work this way!
Consider this example:
bool putOnTheSpaceSuits() { /* ... */ }
bool openTheAirlock() { /* ... */ }
void tryGoIntoSpace(bool spaceSuitsOn, bool airlockOpen) {
if(spaceSuitsOn && airlockOpen) {
spacewalk();
}
}
What happens when we run tryGoIntoSpace(putOnTheSpaceSuits(), openTheAirlock())
? On my machine, openTheAirlock()
is evaluated first, dumping our unprotected astronauts into space. Oops!
Your original question uses two implicit conversions; it's equivalent to std::tuple<X,Y> t(X(1),Y(2));
. You can see the same effect with any random free function that takes an X
and a Y
:
void frob(X x, Y y) { /* ... */ }
frob(X(1), Y(2)); // It's unspecified, but I bet Y(2) will happen first here.
See for yourself: http://coliru.stacked-crooked.com/a/e4142f3c8342ebf2
The fact that you're using a recursively-templated tuple constructor isn't relevant here; all C++ functions are alike. Ideally your function arguments should not have interesting, mutually-interacting side effects, but if that's impossible, you have to do the ordering yourself:
X x(1);
Y y(2);
std::tuple<X,Y> t(x, y);