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 t
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_ptrwas 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,
dmust be left intact for the cleanupd(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 thenewthrows,dwill be left in its original state.If we go further and allow
Dto have a throwing move constructor, things get more complicated. But we won't. :-)
The difference between deleters in std::shared_ptr and std::unique_ptr is that shared_ptr deleter is type-erased, while in unique_ptr deleter type is part of the template.
Here is Stephan T. Lavavej explaining how type erasure leads to CopyConstructible requirement in std::function.
As for the reason behind this difference in pointer types, it has been addressed on SO several times, e.g. here.
A quote of what S.T.L. said:
Very surprising "gotcha" I would say is that the
std::functionrequires CopyConstructible function objects, and this is kind of unusual in the STL.Usually the STL is lazy in the sense that it doesn't need things up front: if I have something like a
std::listof typeT,Tdoes not need to be less-than-comparable; only if you call the member functionlist<T>::sortthen it actually does need to be less-than-comparable.The core language rule that powers this is that the definitions of member functions of a class template are not instantiated until they're actually needed and the bodies don't exist in some sense until you actually call it.
This is usually cool - this means you only pay for what you need, but
std::functionis special because of type erasure, because when you construct thestd::functionfrom some callable objectFit needs to generate everything you could ever need from that objectFbecause it's going to erase its type. It requires all the operations that it could possibly ever need regardless of if they're used.So if you construct a
std::functionfrom some callable objectF,Fis absolutely required at compile-time to be CopyConstructible. This is true even thoughFwill be moved into thestd::function, so even if you give it an r-value and even if you never copystd::functions anywhere in your program,Fis still required to be CopyConstructible.You'll get a compiler error saying so - maybe horrible, maybe nice - depending on what you get.
It just cannot store movable only function objects. This is a design limitation caused in part by the fact that
std::functiondates back to boost/TR1, before r-value references, and in some sense it can never be fixed withstd::function's interface as it stands.Alternatives are being investigated, maybe we can have a different "movable function", so we will probably get some sort of type-erased wrapper that can store movable only function in the future, but
std::functionas it stands in c++17 right now cannot do that, so just be aware.
Because shared_ptr are meant to be copied, and any of those copy could have to delete the object, so they must all have access to a deleter. Keeping only one deleter would require, well, refcounting the deleter itself. If you really want that to happen, you could use a nested std::shared_ptr as the deleter, but that sounds a bit overkill.