Gabriel
Gabriel

Reputation: 9432

reference to std::shared:ptr to avoid reference counting

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

Answers (2)

ecatmur
ecatmur

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

Caleth
Caleth

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 ints nor const ints pointed to by any of the pointers.

Upvotes: 0

Related Questions