I want to understand how the implementation of std::function
works. For simplicity, let's consider move-only functions with no arguments.
I understand that std::function
erases the type of its target through typical type erasure techniques:
template<class Result>
struct function
{
public:
template<class Function>
function(Function&& f)
: f_(std::make_unique<callable_base>(std::forward<Function>(f)))
{}
// XXX how to implement constructor with allocator?
template<class Alloc, class Function>
function(const Alloc& alloc, Function&& f);
Result operator()() const
{
return (*f_)();
}
private:
struct callable_base
{
virtual Result operator()() const = 0;
virtual ~callable_base(){}
};
template<class Function>
struct callable
{
mutable Function f;
virtual Result operator()() const
{
return f;
}
};
// XXX what should the deleter used below do?
struct deleter;
std::unique_ptr<callable_base, deleter> f_;
};
I'd like to extend the functionality of this type to support custom allocation. I'll need to erase the type of the allocator, but that's difficult to do with the use of the std::unique_ptr
. The custom deleter given to the unique_ptr
needs to know the concrete type of the Function
given to the constructor to be able to properly deallocate its storage. I could use another unique_ptr
to type erase the deleter, but that solution is circular.
It seems like the callable<Function>
needs to deallocate itself. What's the correct way to do that? If I deallocate inside of callable<Function>
's destructor, that seems too early because its members are still alive.