-3

I'm trying to construct a pair, from two values read from a stream. These values need to be read in the correct order (T1, then T2), but I believe the order of argument evaluation is undefined in something like the following:

std::pair<T1, T2> Read(DataStreamRead& stream)
{
    return std::pair<T1, T2>(IO::Read<T1>(stream), IO::Read<T2>(stream));
}

(Using intermediate variables works fine, but is there another way?)

user673679
  • 1,327
  • 1
  • 16
  • 35
  • 3
    Something something list initialization, but I'm a bit ashamed to even say it. – chris Dec 20 '17 at 13:08
  • 5
    Any solution would be obscure fragile and difficult to understand intent: in a bit of glue code, just be explicit about order. – Yakk - Adam Nevraumont Dec 20 '17 at 13:10
  • 2
    The obvious solution may also lead to problems later if someone, not privy to the difference and why that solution was used, decided to revert it and introduced UB back into the code. So then you can comment it. But that's an extra line, by which point, you might as well just trade it for the 2 extra lines needed to explicitly get the variables in the right order! (Perhaps there's some case where avoiding those favours optimisation, but the compiler can probably elide that, and anyway, worrying about it without profiling/figures is of course premature.) – underscore_d Dec 20 '17 at 13:15
  • I could even imagine that the compiler would transform code that uses intermediate values into something that looks more or less like what you have there. Evaluation order is undefined, but that doesnt mean that the compiler cannot know in what order they are evaluated. Ergo dont worry about that little inefficiency at the cost of unreadable code – 463035818_is_not_an_ai Dec 20 '17 at 13:16
  • @chris. What does that mean? Brace initialization doesn't change the order of evaluation, though I've found another post that suggests this is a compiler bug: https://stackoverflow.com/questions/14060264/order-of-evaluation-of-elements-in-list-initialization – user673679 Dec 20 '17 at 13:42
  • 2
    @user673679 list-init does fix the evaluation order since c++11; everybody fear exploiting this fact (for the reason pointed out by Yakk) but, also in light of c++17 new ordering guarantees, sooner or later these will become idiomatic ... I wonder if it's still legit to consider such use "obscure and fragile"... dunnow – Massimiliano Janes Dec 20 '17 at 13:58
  • 1
    @user673679, This is more current than C++11 or anything, but hopefully good enough: http://eel.is/c++draft/dcl.init.list#4. I certainly don't recommend it because it's unclear that the braces are there for this purpose specifically. I'd consider an `eval_in_order{a, b, c}`, but there's no way to write such a thing where you could seamlessly use it in place of multiple arguments. – chris Dec 20 '17 at 14:21
  • 1
    @MassimilianoJanes, True, C++17 changed some things. I'm hesitant to say that those kind of changes will ever be applied uniformly. We're in a bit of an awkward state now where some constructs have more guarantees than before, but some are left untouched. For example, given an `int a = 1;`, `std::cout << a++ << a++;` is well-defined and prints 12. `a++ << a++` is also well-defined and evaluates to 4. `a++ * a++` is still undefined. For functions, `f(a++, a++)` is now well-defined, but the order of evaluation is still unspecified. – chris Dec 20 '17 at 14:26
  • 1
    @chris agreed; personally, I don't know what to think about this; the new rules are there in order to permit useful idioms (like continuations and other chainable things) so we (as users of the language) cannot just 'ignore' them; sooner or later people should start to take them seriously and act consequently ... or not ? – Massimiliano Janes Dec 20 '17 at 14:43

1 Answers1

2

Order the operations explicitly.

std::pair<T1, T2> Read(DataStreamRead& stream)
{
  T1 t1 = IO::Read<T1>(stream);
  T2 t2 = IO::Read<T2>(stream);
  return std::pair<T1, T2>(t1, t2);
}
Eljay
  • 4,648
  • 3
  • 16
  • 27