Not sure if this is what you're looking for. But you can get a lot of the functionality of a "dynamic deleter" by using a function pointer as a deleter with unique_ptr
:
std::unique_ptr<Base, void(*)(void*)> p(allocate<Derived>(), deallocate<Derived>);
The deleter is just a function. But at run time you can feed it a pointer to a templated function, templated on your Derived
type. It allows the right destructor to be called, even if the destructor isn't virtual. Though anything else called via this pointer must be virtual, or it will resolve to the Base
class of course. Here's a full HelloWorld:
#include <memory>
#include <new>
#include <cstdlib>
template<typename T>
T*
allocate()
{
std::unique_ptr<T, void(*)(void*)> hold(static_cast<T*>(std::malloc(sizeof(T))),
std::free);
::new (hold.get()) T;
return static_cast<T*>(hold.release());
}
template<typename T>
void
deallocate(void* p)
{
static_cast<T*>(p)->~T();
std::free(p);
}
#include <iostream>
struct Base
{
Base() {std::cout << "Base()\n";}
Base(const Base&) = delete;
Base& operator=(const Base&) = delete;
~Base() {std::cout << "~Base()\n";}
virtual void bark() const {std::cout << "Hi Base!\n";}
};
struct Derived
: public Base
{
Derived() {std::cout << "Derived()\n";}
Derived(const Base&) = delete;
Derived& operator=(const Derived&) = delete;
~Derived() {std::cout << "~Derived()\n";}
void bark() const {std::cout << "Hi Derived!\n";}
};
int
main()
{
std::unique_ptr<Derived, void(*)(void*)> p(allocate<Derived>(), deallocate<Derived>);
p->bark();
std::unique_ptr<Base, void(*)(void*)> p2 = std::move(p);
p2->bark();
}
Base()
Derived()
Hi Derived!
Hi Derived!
~Derived()
~Base()