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::function
requires 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::list
of type T
, T
does not need to be less-than-comparable; only if you call the member function list<T>::sort
then 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::function
is special because of type erasure, because when you construct the std::function
from some callable object F
it needs to generate everything you could ever need from that object F
because 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::function
from some callable object F
, F
is absolutely required at compile-time to be CopyConstructible. This is true even though F
will be moved into the std::function
, so even if you give it an r-value and even if you never copy std::function
s anywhere in your program, F
is 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::function
dates back to boost/TR1, before r-value references, and in some sense it can never be fixed with std::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::function
as it stands in c++17 right now cannot do that, so just be aware.