I have been looking through the Clang source code and I found this snippet:
void CompilerInstance::setInvocation(
std::shared_ptr
Move operations (like move constructor) for std::shared_ptr
are cheap, as they basically are "stealing pointers" (from source to destination; to be more precise, the whole state control block is "stolen" from source to destination, including the reference count information).
Instead copy operations on std::shared_ptr
invoke atomic reference count increase (i.e. not just ++RefCount
on an integer RefCount
data member, but e.g. calling InterlockedIncrement
on Windows), which is more expensive than just stealing pointers/state.
So, analyzing the ref count dynamics of this case in details:
// shared_ptr sp;
compilerInstance.setInvocation(sp);
If you pass sp
by value and then take a copy inside the CompilerInstance::setInvocation
method, you have:
shared_ptr
parameter is copy constructed: ref count atomic increment.shared_ptr
parameter into the data member: ref count atomic increment.shared_ptr
parameter is destructed: ref count atomic decrement.You have two atomic increments and one atomic decrement, for a total of three atomic operations.
Instead, if you pass the shared_ptr
parameter by value and then std::move
inside the method (as properly done in Clang's code), you have:
shared_ptr
parameter is copy constructed: ref count atomic increment.std::move
the shared_ptr
parameter into the data member: ref count does not change! You are just stealing pointers/state: no expensive atomic ref count operations are involved.shared_ptr
parameter is destructed; but since you moved in step 2, there's nothing to destruct, as the shared_ptr
parameter is not pointing to anything anymore. Again, no atomic decrement happens in this case.Bottom line: in this case you get just one ref count atomic increment, i.e. just one atomic operation.
As you can see, this is much better than two atomic increments plus one atomic decrement (for a total of three atomic operations) for the copy case.