Instead of trying to reason about the copying and moving of huge objects, I give my brain a break by using std::unique_ptr<HugeObject>. unique_ptr is moveable, cannot accidentally be copied (because its copy constructor is private), and is trivially small. unique_ptr also gets around the problem where the compiler can't figure out which value is being RVO'd, because you can do this:
> I give my brain a break by using std::unique_ptr<HugeObject>.
Unfortunately this comes at a cost. Most notably, now you're unnecessarily stressing the allocator. But worse, you no longer enjoy the benefits of value semantics [1].
This doesn't help to solve the original problem of avoiding the copy. Your calls to
big_object_ptr.reset(...)
will cause the BigObject copy constructor to be called. The object pointed to by unique_ptr does not have the same storage as the objects "foo" and "bar". At the very least, you need to do
big_object_ptr.reset(std::move(foo));
which will cause foo to be moved into the unique_ptr's storage.
In code like this, you should just use make_unique, in which case the BigObject is constructed in place, and unique_ptr can be RVO'ed:
if (...) {
return make_unique<BigObject>(...);
} else ...
Yeah I realized after the fact that the example is pointless, but I was trying to adhere to the original post, which is also pointless. Why would you construct two BigObject when you know you will discard one of them unused? It's dumb.
You can actually just do
return big_object_ptr;
Instead of the std::move.
The compiler will complain if it can't do RVO because unique_ptr prohibits a copy, so you're safe :)