Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do shared_ptr deleters have to be CopyConstructible?

In C++11 std::shared_ptr has four constructors which can be passed deleter objects d of type D. The signatures of these constructors are the following:

template<class Y, class D> shared_ptr(Y * p, D d); template<class Y, class D, class A> shared_ptr(Y * p, D d, A a); template <class D> shared_ptr(nullptr_t p, D d); template <class D, class A> shared_ptr(nullptr_t p, D d, A a); 

The standard requires in [util.smartptr.shared.const] type D to be CopyConstructible. Why is this needed? If shared_ptr makes copies of d then which of these deleters might get called? Wouldn't it possible for a shared_ptr only to keep a single deleter around? What does it mean for a shared_ptr to own a deleter if d can be copied?

What is the rationale behind the CopyConstructible requirement?

PS: This requirement might complicate writing deleters for shared_ptr. unique_ptr seems to have much better requirements for its deleter.

like image 998
jotik Avatar asked Apr 20 '16 12:04

jotik


People also ask

What is the purpose of the Shared_ptr <> template?

std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer.

What happens when Shared_ptr goes out of scope?

The smart pointer has an internal counter which is decreased each time that a std::shared_ptr , pointing to the same resource, goes out of scope – this technique is called reference counting. When the last shared pointer is destroyed, the counter goes to zero, and the memory is deallocated.

Can you move Shared_ptr?

By moving the shared_ptr instead of copying it, we "steal" the atomic reference count and we nullify the other shared_ptr . "stealing" the reference count is not atomic, and it is hundred times faster than copying the shared_ptr (and causing atomic reference increment or decrement).


1 Answers

This question was perplexing enough that I emailed Peter Dimov (implementer of boost::shared_ptr and involved in standardization of std::shared_ptr)

Here's the gist of what he said (reprinted with his permission):

My guess is that the Deleter had to be CopyConstructible really only as a relic of C++03 where move semantics didn’t exist.

Your guess is correct. When shared_ptr was specified rvalue references didn't exist yet. Nowadays we should be able to get by with requiring nothrow move-constructible.

There is one subtlety in that when

pi_ = new sp_counted_impl_pd<P, D>(p, d); 

throws, d must be left intact for the cleanup d(p) to work, but I think that this would not be a problem (although I haven't actually tried to make the implementation move-friendly).
[...]
I think that there will be no problem for the implementation to define it so that when the new throws, d will be left in its original state.

If we go further and allow D to have a throwing move constructor, things get more complicated. But we won't. :-)

like image 124
AndyG Avatar answered Sep 23 '22 00:09

AndyG