Brian Rodriguez
Brian Rodriguez

Reputation: 4376

Tuple isn't being constructed in order?

The following program:

#include <iostream>
#include <tuple>

struct A {
    A() { std::cout << "A constructor\n"; }
};

struct B {
    B() { std::cout << "B constructor\n"; }
};

int main() {
    std::tuple<A, B> t;
}

gives different outputs on different compilers:

# libstdc++
B constructor
A constructor
# libc++
A constructor
B constructor

This seem weird... I figured the standard would have guaranteed the tuple elements be constructed in-order, e.g., A, B, ..., Y, Z?

Upvotes: 7

Views: 510

Answers (2)

Brian Rodriguez
Brian Rodriguez

Reputation: 4376

std::tuple construction order is currently unspecified.

A proposal for a concrete decision on its order has been submitted to the committee but until then the order should not be relied on.

Upvotes: 6

David Seiler
David Seiler

Reputation: 9725

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

Upvotes: 2

Related Questions