In the code you have written the task
object will be destroyed (with a call to the destructor) at the end of pass_back_ownership
.
That is completely fine and no UB in your code. However, main
must be careful not to dereference ptr
after the call to pass_back_ownership
.
Also be careful that there are potential order-of-evaluation issues if pass_back_ownership
were to take the parameter by-value instead of by-reference. That would make the code a bit simpler, since you wouldn't need to crate the local tmp
variable, but only since C++17 is it guaranteed that ptr->pass_back_ownership
will be evaluated before the function parameter is constructed. If the order was the other way around, then you would have UB for dereferencing an empty ptr
.
Regarding best practice:
What your call ptr->pass_back_ownership(std::move(ptr));
is saying is that main
relinquishes ownership of the task
object referenced by ptr
to itself. The lifetime of the task
object is not intended to be bound to the scope of main
anymore.
The question would then be what possible lifetimes the task
object could then have. There are basically only two choices: 1. its lifetime ends at latest at the end of the function call, which is your case, or 2. pass_back_ownership
stores the passed unique_ptr somewhere outside the function to extend the lifetime.
If you are interested only in 1., then I would question why it is necessary to pass the std::unique_ptr
to some random function to achieve the effect. If you intent ptr->pass_back_ownership(std::move(ptr));
to definitively destroy the task
object, then why aren't the operations that pass_back_ownership
does simply done in the destructor of task
? Then you could simply let ptr
in main
run out-of-scope to have the same effect.
It makes a bit more sense if pass_back_ownership
only conditionally actually takes ownership by moving the unique_ptr
reference. Then main
could test with if(ptr)
after the call whether or not the task
object was destroyed, but then why is it important for the task
object to be destroyed prematurely instead of simply waiting for ptr
in main
to go out-of-scope? That no instructions are left can also be communicated by either having the function return a bool
state or have the task
object have some way of checking presence of instructions (e.g. an operator bool
).