In f(x), can x be evaluated before f?

I have a C++ program. This program does something like this:

struct MyT {void memfunc(std::unique_ptr<MyT> arg);};
std::unique_ptr<MyT> obj = /* some init */;
obj->memfunc(std::move(obj));

Is this guaranteed to be valid, or can I end up calling a member function on nullptr?
Standard quotes applicable.
I know that the order of evaluation of the arguments is unsequenced, but I don't recall what the sequencing is w.r.t. the function object being called.


Pre-C++17, it is undefined behavior:

Here the quote which proves that all evaluation neccessary to call a function and associated side-effects are sequenced before the function call.
Also, all other evaluations not specifically sequenced are indeterminately sequenced.
That does not impose any ordering constraints on the sub-expressions evaluated though with respect to each other, they remain unsequenced with respect to each other.

1.9 Program execution § 15

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
[...]
The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either anotherside effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function.
[ Note: Value computations and side effects associated with different argument expressions are unsequenced. —end note ]
Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.9 Several contexts in C++ cause evaluation of a function call, even though no corresponding function call syntax appears in the translation unit. [ ... ]
The sequencing constraints on the execution of the called function (as described above) are features of the function calls as evaluated, whatever the syntax of the expression that calls the function might be.

Other relevant quotes are about std::move

template typename remove_reference::type&& move(T&& t) noexcept;
Returns: static_cast<typename remove_reference::type&&>(t).

And std::unique_ptr<T>.operator->():

20.7.1.2.4 unique_ptr observers

pointer operator->() const noexcept;
Requires: get() != nullptr.
Returns: get().

memfunc gets its argument by value, so we have 3 calls:
a) obj->memfunc b) std::move(obj)
c) the move constructor of the passed argument.
Because b) does not change anything, we can disregard it for the argument:

a and c are indeterminately sequenced, so either can be before the other.
If a happens first, everything is good, c changing obj does not matter.
If c happens first, a is evaluated with a zeroed obj, violating the precondition, so we have UB.

In summary, it is Undefined Behavior, because one of the allowed orders has undefined behavior.

Post-C++17, it is well-defined:

8.2.2 Function call [expr.call]

1 A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function. [...]
[...]
5 The postfix-expression is sequenced before each expression in the expression-list and any default argument. [...]
[...]


Yes, the evaluation of x can occur before, after, or during the evaluation of f (they are unsequenced).

[ Note: The evaluations of the postfix expression and of the argument expressions are all unsequenced relative to one another. All side effects of argument expression evaluations are sequenced before the function is entered (see 1.9). — end note ]

(C++11, §5.2.2/8)