1

Say I have code:

struct A {int val, string name};
A a{5, "Hello"};
fn(a.val, std::move(a));

Now it looks like I'm reading from a and also moving from a on the same line which looks bad as parameter ordering is generally undefined. However, as std::move is just a cast that should be ok - so I'm not actually reading from a moved from value.

What happens though if fn actually takes the second parameter by value:

fn(int first, A second);

In this case a new A must be constructed from the moved from value a. Would this start causing issues in my first example? I assume the constructor of the parameter second may be called before the first parameter a.val is read?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Mike Vine
  • 9,468
  • 25
  • 44
  • 2
    The [evaluation order of function arguments is unspecified](https://stackoverflow.com/questions/2934904/order-of-evaluation-in-c-function-parameters) – Cory Kramer Oct 11 '18 at 12:42
  • Yes I understand. And I don't care if `std::move` or a.first is called first. I care if the implicit constructor for the parameter necessary in the second example is called first. I assume it is but its not clear. – Mike Vine Oct 11 '18 at 12:43
  • You're right. Don't rely on this. You can cache the value of `a.val` and pass that to `fn` instead. – François Andrieux Oct 11 '18 at 12:43

1 Answers1

1

The problem that you are going to run into with this is [expr.call]/8

The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.

emphasis mine

So, what could happen then is second is initialized first, which will be move constructed from a so it will steal its guts. This means a is in a valid but indeterminate state when you go to initialize first. Using first after that would be undefined behavior. Now if the order is reversed you are fine but since the order is not specified you are not guaranteed any consistent behavior.

You are right that std::move isn't actually moving anything but initializing second with a object that was passed to std::move will cause second to use it's move constructor and that will actually move a.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Perfect, it was that *including every associated value computation and side effect* that I was looking for. – Mike Vine Oct 11 '18 at 13:11