Reputation: 9432
I am often facing the argument, that by accepting a const std::shared_ptr<T>&
one can avoid the reference count increment:
void foo(std::shared_ptr<const int> p);
void foo2(const std::shared_ptr<const int>& p);
int main(){
std::shared_ptr<const int> a = std::make_shared<int>(3);
foo(a); // calling this function always does reference counting (atomic locks...)
foo2(a); // calling this function elides reference counting
std::shared_ptr<int> b = std::make_shared<int>(3);;
foo2(b); // what happens here? since there is a cast involved creating a temporary ... (NRVO??)
}
But I assume that when calling foo2(b)
reference counting is not elided? However, can the compiler or the std-implementation somehow elide reference counting. Would it make it any better if foo2(std::move(b))
would be called, for this elision to happen?
Upvotes: 1
Views: 891
Reputation: 157344
Yes, there will necessarily be a reference count increment prior to entry to foo2
and a decrement on exit. This is because the parameter const std::shared_ptr<const int>& p
must refer to a distinct object of type std::shared_ptr<const int>
, so a temporary must be constructed and destroyed.
Within the language it would not be possible to elide this reference count, because if the argument b
is modified during the execution of foo2
the parameter p
must remain unaffected; it must continue to point to the same int
object on the heap, which may be modified but not deleted. (This does not apply when foo2
is called with a
, as in that case p
refers directly to a
, not to a temporary, so modifications to a
are visible through p
.)
Passing std::move(b)
to foo2
would not be a good idea, because this will leave b
empty and delete the int
object when the temporary bound to p
is destructed.
Upvotes: 3
Reputation: 62684
std::shared_ptr<const int>
is a different type to std::shared_ptr<int>
, but there is an implicit converting constructor.
When you call foo2(b)
, a temporary std::shared_ptr<const int>
is constructed from b
and bound to p
. The constructor increments the reference count, and the destructor decrements it.
When you call foo1(a)
, a
is copied into p
. p
exists for the duration of the call, and is then destructed. The constructor increments the reference count, and the destructor decrements it.
When you call foo2(a)
, a
is bound to p
. No temporary is constructed, so the reference count isn't changed.
Note that there isn't a reference count in your example, as no int
s nor const int
s pointed to by any of the pointers.
Upvotes: 0