Issue
I am developing a program for embedded system that will not use dynamic memory allocation. How can I prevent the GCC from producing deleting destructor (the destructor with D0
in its mangled name)? It will be never called.
I think that there is even no need to have deleting destructors, since the complete object destructor (D1
in mangled name) can be called instead along with operator delete(…)
after it.
In my opinion, there should be some command-line option to disable these destructors.
The main problem is that the program calls operator delete(…)
in the deleting destructor. Since I do not have any heap management, no such function is defined. As a workaround, I could implement operator delete(…)
that just reports an error, but this would waste memory (unnecessarily large program size) and will not allow me to catch accidental call to operator delete()
during the compilation.
Solution
The deleting destructor variant exists so that the syntax
delete ptr;
where ptr
points to an polymorphic type can work even if ptr
doesn't point to the most-derived object. The user of delete ptr;
doesn't know what the offset to the most-derived object or its size is, but it needs to know that in order to call operator delete
correctly. Therefore there needs to be an indirect/virtual call to a function that knows, which is the deleting destructor.
Unfortunately the compiler has to generate the deleting destructor from such a virtual
destructor at least for all classes which are used as most-derived objects, since there could be a delete
expression of that kind in another translation unit that doesn't know about the definition of this most-derived destructor which it must however (indirectly) call.
I don't think that you can separate the delete
behavior of virtual destructors from the ability to do explicit virtual destructor calls and I also don't see any GCC switch to disable the generation of the deleting destructor as a non-standard/ABI conforming option.
So I guess you'll have to avoid virtual destructors. You can get the virtual destruction behavior anyway by forwarding from a virtual
function:
struct Base {
virtual destroy() noexcept { this->~Base(); }
// destructor not virtual
};
struct Derived {
virtual destroy() noexcept override { this->~Derived(); }
};
And then instead of ptr->~Base();
/std::destroy_at(ptr)
you can use ptr->destroy()
.
However, this has the problem that you need to assure that destruct
is properly overriden in every derived class to avoid undefined behavior. CRTP or C++23 explicit object parameters (explicit this
) may help with that.
You also have the problem that someone might accidentally call the destructor directly, again causing undefined behavior. Making the destructor private
is also generally not a solution because the destructor is potentially invoked in many situations, e.g. in a constructor of a class that contains the class in question as a non-static member.
I originally suggested here to implement the operator delete
as empty and inline
in every translation unit to help with removing the calls, however I didn't realize that marking the replaceable deallocation functions as inline
makes the program IFNDR and so isn't valid. (See [replacement.functions]/3.)
You can't delete the operator delete
, because it will be odr-used for the reasons I mentioned above and must therefore viable.
LTO might further help getting rid of unused emitted deleting destructors during linking, at least via devirtualization. But I haven't tested.
Answered By - user17732522 Answer Checked By - Gilberto Lyons (WPSolving Admin)