how to avoid undefined execution order for the constructors when using std::make_tuple
c++, c++11, tuples, variadic-templates
Solution
The trivial solution is not to use `std::make_tuple(...)` in the first place but to construct a `std::tuple<...>` directly: The order in which constructors for the members are called is well defined:
template <typename>
std::istream& dummy(std::istream& in) {
return in;
}
template <typename... T>
std::tuple<T...> parse(std::istream& in) {
return std::tuple<T...>(dummy<T>(in)...);
}
The function template `dummy<T>()` is only used to have something to expand on. The order is imposed by construction order of the elements in the `std::tuple<T...>`:
template <typename... T>
template <typename... U>
std::tuple<T...>::tuple(U...&& arg)
: members_(std::forward<U>(arg)...) { // NOTE: pseudo code - the real code is
} // somewhat more complex
Following the discussion below and Xeo's comment it seems that a better alternative is to use
template <typename... T>
std::tuple<T...> parse(std::istream& in) {
return std::tuple<T...>{ T(in)... };
}
The use of brace initialization works because the order of evaluation of the arguments in a brace initializer list is the order in which they appear. The semantics of `T{...}` are described in 12.6.1 [class.explicit.init] paragraph 2 stating that it follows the rules of list initialization semantics (note: this has nothing to do with std::initializer_list which only works with homogenous types). The ordering constraint is in 8.5.4 [dcl.init.list] paragraph 4.
Problem
How can I use std::make_tuple if the execution order of the constructors is important? For example I guess the execution order of the constructor of class A and the constructor of class B is undefined for: ``` std::tuple<A, B> t(std::make_tuple(A(std::cin), B(std::cin))); ``` I came to that conclusion after reading a comment to the question Translating a std::tuple into a template parameter pack that says that this ``` template<typename... args> std::tuple<args...> parse(std::istream &stream) { return std::make_tuple(args(stream)...); } ``` implementation has an undefined execution order of the constructors. Update, providing some context: To give some more background to what I am trying to do, here is a sketch: I want to read in some serialized objects from stdin with the help of CodeSynthesis XSD binary parsing/serializing. Here is an example of how such parsing and serialization is done: example/cxx/tree/binary/xdr/driver.cxx ``` xml_schema::istream<XDR> ixdr (xdr); std::auto_ptr<catalog> copy (new catalog (ixdr)); ``` I want to be able to specify a list of the classes that the serialized objects have (e.g. catalog, catalog, someOtherSerializableClass for 3 serialized objects) and store that information as a typedef ``` template <typename... Args> struct variadic_typedef {}; typedef variadic_typedef<catalog, catalog, someOtherSerializableClass> myTypes; ``` as suggested in Is it possible to “store” a template parameter pack without expanding it? and find a way to get a std::tuple to work with after the parsing has finished. A sketch: ``` auto serializedObjects(binaryParse<myTypes>(std::cin)); ``` where serializedObjects would have the type ``` std::tuple<catalog, catalog, someOtherSerializableClass> ```
Related problems
- Is it possible to "store" a template parameter pack without expanding it?
- Is there a way to force a specific evaluation order of function arguments?
- Translating a std::tuple into a template parameter pack
- Call function with parameters extracted from string
- Tuple isn't being constructed in order?
- How to guarantee order of argument evaluation when calling a function object?
- Is there a way to force a specific evaluation order of function arguments?