First of all, I am not sure about the initial rationale for ~
in ~T()
. The thing is that most of the time it does not matter how the destructor is called manually.
Usually you never call a destructor explicitly. Most of your variables should use automatic storage:
{
foo x;
}
The destructor of x
is called automatically when x
goes out of scope. Explicitly calling the destructor of x
is wrong, because once it goes out of scope it would be called again resulting in undefined behavior.
Sometimes you need dynamic allocation (do it via smart pointers, but for the point here manual is "ok"):
foo* p = new foo();
delete p;
You must delete
what you created via new
. Also here calling the destructor explicitly would be wrong, because delete
calls the destructor and frees the allocated memory. Only calling the destructor will not free the memory and calling the destructor twice is undefined behavior.
The only case you have to call a destructor explicitly is when you allocate memory and create the object in two seperate steps via placement new: What uses are there for "placement new"?.
Placement new isn't something you use everyday. It is mainly found in libraries. It is a case where you need to call a destructor explicitly and that is the case when std::destroy_at
can be handy.
Suppose you write this code:
void foo() {
auto x = returns_some_pointer();
// ....
... now delete *x ...
}
Suppose returns_some_pointer
returns a pointer to some object. Further suppose that object has been created via placement new and at the end of foo
we want to call its destructor. foo
doesn't need to know the type of x
for anything. To call the destructor it would need to know T
to be able to call ~T()
. The type can be deduced from x
itself, but thats not very convenient. A very convenient way to deduce T
is to use a function template, and that is destroy_at(x);
.
TL;DR: For most cases it does not really matter how the destructor is called, because it will be either called automatically or under the hood by calling delete
. std::destroy_at
is not a replacement for the destructor, but rather a convenience method to deduce the type of the object whose destructor is to be called.