How to assign a destructor to a pointer?

Viewed 231

I'm writing a memory management library and need to call a destructor explicitly, in my design I have a pointer which points to the destuctor method of a object, I write code like this:

void (*p)() = foo.~Foo;

but I got

error: cannot convert ‘Foo::~Foo’ from type ‘void (Foo::)() noexcept’ to type ‘void (*)()’

I tried some other format like void (Foo:: (*p))() noexcept = foo.~Foo; but failed.

So what is the right way to assignment a destructor to a pointer?

Edit: at runtime my code do not know what a type it is in my heap, so I can't use foo.~Foo(). I need a generate pointer which fits for all destructors, is this possible?

4 Answers

Solution: Use a template wrapper

Taking the address of the destructor isn't allowed.

However you can just make a very simple template function and use that instead:

template<class T>
void destruct(const T* x) {
    x->~T();
}

Now instead, just obtain the pointer from:

destruct<Foo>

You can use eg. std::bind (or a lambda) if you need to bind to an actual object:

std::bind(&destruct<Foo>, foo_ptr);

Please note that once it is bound, it cannot be converted to a raw function pointer.

Sorry, you can't. You are not allowed to take the address of a destructor per [class.dtor]/2:

A destructor is used to destroy objects of its class type. The address of a destructor shall not be taken. A destructor can be invoked for a const, volatile or const volatile object. const and volatile semantics ([dcl.type.cv]) are not applied on an object under destruction. They stop being in effect when the destructor for the most derived object starts.

I need a generate pointer which fits for all destructors, is this possible?

No, it is not. Like it would not be with any other member function.

To call a function on an object, you need to know the type of that object.

And, since you cannot take the address of a destructor, you cannot even store/register one in a "database". However, as Paul showed, you can store a functor to do the job. It'll be a bit ugly to register these for each object in use, but that's what happens when you try to reinvent the type system!

I strongly advise moving away from type erasure (how about some nice inheritance instead?), and moving away from calling destructors yourself.

Remember that the destructor is, of course, a member function so you'll need two pointers: one for the object and one for the offset of the member function (the destructor in this case).

To get the object's address is trivial, simply use std::addressof().

But to get to the destructor you'll need a wrapper function to invoke the destructor given the object's address because you can't take the address of a destructor.

So use something like Herb Sutter suggests (thanks to @LightnessRacesinOrbit's and @Croolman's comments):

[](const void* x) { static_cast<const Foo*>(x)->~Foo(); }
Related