48

I understand that in most cases, we should not call a destructor explicitly. However, I saw an example from C++11 Standard N3485 Section 13.4.5 Template arguments:

An explicit destructor call for an object that has a type that is a class template specialization may explicitly specify the template-arguments. Example:

template<class T> struct A {
    ~A();
}; 

void f(A<int>* p, A<int>* q) {
    p->A<int>::~A();      // OK: destructor call
    q->A<int>::~A<int>(); // OK: destructor call
}

It seems to me that we can call destructor explicitly in this case, could you explain to me why? What does those destructor call mean in this example? Why they are reasonable?

Another question:

What are the cases that we can call destructors explicitly besides when we are implementing placement delete?

Thank you.

EDIT: I found from C++ FAQ that we should not explicitly call a destructor on a local variable.

Community
  • 1
  • 1
taocp
  • 23,276
  • 10
  • 49
  • 62
  • 4
    The referenced post is about calling a destructor explicitly on a local variable, whose destructor is also called again implicitly at the end of its scope. – Andy Thomas May 23 '13 at 17:30
  • +1 i have done some similar nonsense in my code, calling the destructor explicitly to destroy my object(or atlest it seems to work). but i've done it on managed types. will be interesting to see the answers – Koushik Shetty May 23 '13 at 17:31
  • @AndyThomas-Cramer I understand, what I meant is the explanation from answers that quotes the standard about explicit destructor calls. Is my post misleading? I will try to reword it if this is a problem. – taocp May 23 '13 at 17:32
  • 1
    @taocp - Just clarifying here, as in the referenced post, that the undefined behavior is not calling a destructor explicitly - it's causing it to be called on the same object *more than once*. – Andy Thomas May 23 '13 at 17:42
  • @AndyThomas-Cramer ah, I agree. I looked at the answers mainly. I will try to correct it. Thank you! – taocp May 23 '13 at 17:43
  • @taocp could you add about the differences in calling dtors explicitly on auto and allocated objects too? it would be good if this question can cover these issues so i can link back. Thank you – Koushik Shetty May 23 '13 at 17:47
  • @Koushik I am not sure whether I understood your comments correctly. So I did a `blind` edit by adding explicit destructor call on local objects. I seems can't find resources about explicitly dtor call on auto and allocated objects. – taocp May 23 '13 at 17:51
  • @AndyThomas-Cramer: Correction of the correction :) UB is when you invoke the destructor of an object that already ceased to exist. For objects with a non-trivial destructor, the moment they cease to exist is when the destructor is called - but for objects with trivial destructors, that's when the object's storage is reused or released – Andy Prowl May 23 '13 at 17:56
  • Some example: of VS2019 : template void destroy_at(_Ty* const _Location) { // destroy _Ty at memory address _Location _Location->~_Ty(); } – jw_ Feb 09 '20 at 00:56

2 Answers2

35

It seems to me that we can call destructor explicitly in this case, could you explain to me why?

Do you mean why can we? Because the language allows explicit destructor calls on any object. As you say, it usually gives undefined behaviour since most objects will be destroyed in some other way, and it's undefined behaviour to destroy anything twice (or more generally to access it after destruction). But that just means that you mustn't do it, not that the language will prevent you from doing it.

Or do you mean why would we want to? Because that's how you destroy an object created by placement new.

What does those destructor call mean in this example?

They both mean the same thing, and are equivalent to p->~A(); they call the object's destructor. The example is demonstrating that you can provide template arguments here if you want to. I'm not sure why you'd want to.

What are the cases that we can call destructors explicitly besides placement delete?

I think that you're allowed to call a trivial destructor (one that doesn't do anything) whenever you like; but there's no point. I think destroying something created with placement new is the only legitimate reason to do it.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • does the same hold true for objects with allocated storage?i had done this once on Win form where i call the destructor explicitly to destroy the Form object, but if its UB then i know i have to change the code now. – Koushik Shetty May 23 '13 at 17:53
  • 1
    +1. Also, you're correct about trivial destructors, since §3.8/1 defines that the lifetime of an object with a trivial destructor does not end when the destructor is called, but when storage for that object is reused or released – Andy Prowl May 23 '13 at 17:54
  • @AndyProwl could you give closure to my comment.:) – Koushik Shetty May 23 '13 at 18:00
  • @Koushik: Is Win form .NET stuff? That's unknown territory for me :) – Andy Prowl May 23 '13 at 18:01
  • @AndyProwl oh yes it is:) just clarify this andy. if you dont call delete on a allocated object but instead call its destructor how good is the program? – Koushik Shetty May 23 '13 at 18:04
  • @Koushik: Well, in that case I really don't know how .NET handles these things with the garbage collection stuff and so on. If you call the destructor manually and then do not call `delete` and the object was allocated with (non-placement) `new`, then you have a memory leak, and that's almost always a bad thing – Andy Prowl May 23 '13 at 18:07
  • @AndyProwl ah yes this is what my mistake was. i had it in my mind that calling destructor would free be ok. Thanks andy, mike and taocp. i have lot of clean up to do :) – Koushik Shetty May 23 '13 at 18:10
  • If you have member anonymous union, with member-fields that have non-trivial destructors, then you would manually need to call the destructor of those member-fields in your class destructor (as the compiler doesn't know which destructor to call). – Mark Ingram Nov 19 '13 at 15:21
  • What about this: of VS2019 : template void destroy_at(_Ty* const _Location) { /* destroy _Ty at memory address _Location*/ _Location->~_Ty(); } – jw_ Feb 09 '20 at 00:58
30

It seems to me that we can call destructor explicitly in this case, could you explain to me why?

Because it's allowed by the language to be able to invoke the destructor of any object whenever you want (assuming you have access, e.g. it's not a private destructor).

What does those destructor call mean in this example?

It just invokes the destructor. Logically, it means that the object is destructed and should be considered to be garbage from that point on and should not be dereferenced or used. Technically it means that the object is in whatever state the destructor leaves it in, which for some objects may be identical to default construction (but you should never, ever rely on that).

Why they are reasonable?

Sometimes you need to destroy objects without releasing their memory. This happens in a lot of class like variant/any, various script binding and reflection system, some singleton implementations, etc.

For example, you might use std::aligned_storage to allocate a buffer for an object and then use placement new to construct an object in that buffer. You cannot call delete on this object since that will both invoke the destructor and try to free the memory backing it. You must explicitly invoke the destructor in this case to properly destruct the object.

What are the cases that we can call destructors explicitly besides placement delete?

There's not really such a thing as 'placement delete', other than the corresponding operator to placement new (and any calls to delete will implicitly invoke the destructor except those the compiler invokes for failed construction, e.g. your 'placement delete' notion).

One example I gave above. Another example is std::vector. You can call member functions like pop_back(). This needs to destroy the last element in the vector but it can't use delete since the memory backing the object is part of a larger buffer that must be managed separately. The same goes for many other containers, like open-addressing hash tables, deque, and so on. This is an example of where you'd want to use the template typename in order to invoke the destructor explicitly.

It's a feature that a user of a library is very rarely going to need but the implementor of a low-level library like the STL or even some application frameworks is going to need to use here and there.

the swine
  • 10,713
  • 7
  • 58
  • 100
Sean Middleditch
  • 2,517
  • 18
  • 31