Question

Use after move in function call

When exactly does an object cast with std::moved get moved? For instance, does the following code constitute use after move?

f(std::move(a), a.Something())

Where f = f(A a, int x) and a is an instance of some class.

I understand that std::move(a) may be evaluated before a.Something(), but when does the actual move itself occur?

 3  114  3
1 Jan 1970

Solution

 4

When exactly does an object cast with std::moved get moved?

It depends on how the reference returned by std::move() is being used. std::move() itself doesn't do anything, it is just a type-cast, nothing more. The actual move happens when something else receives that reference and decides to move the refered-to object's data.

For instance, does the following code constitute use after move?

f(std::move(a), a.Something())

Where f = f(A a, int x) and a is an instance of some class.

It CAN, but the order of evaluation of function parameters is not guaranteed. std::move(a) MAY be evaluated first, or a.Something() MAY be evaluated first. What is guaranteed is only that both parameters will have been fully evaluated before f() is actually entered.

I understand that std::move(a) may be evaluated before a.Something(), but when does the actual move itself occur?

Since the A a parameter is being passed into f() by value, a temporary object must be constructed for it. A move CAN happen if the return value of std::move() is used to move-construct that object, but only if the A(A&&) constructor is implemented (either implicitly by the compiler, or explicitly in A's code), and it actually performs a move. Without that constructor, the object will be copy-constructed instead, and thus no move will happen.

2024-07-18
Remy Lebeau

Solution

 2

This line:

f(std::move(a), a.Something());

Can indeed constitute a use after move.

If your f is:

f(A a, int x) { ... }

Then the first argument will have to be either copied or moved into the parameter A a before invoking f. This means that the actual move can also take place before a.Something() is evaluated, causing a use after move.

It's worth nothing that if your f was e.g.:

f(A && a, int x) { ... }

Then you would be safe, because std::move itself is just a cast to rvalue ref which is what is expected for A && a, and therefore no move would actually take place at the stage of evaluating the arguments for f.

2024-07-18
wohlstad