Can a destructor be recursive?

Cubbi picture Cubbi · Jun 17, 2010 · Viewed 7.6k times · Source

Is this program well-defined, and if not, why exactly?

#include <iostream>
#include <new>
struct X {
    int cnt;
    X (int i) : cnt(i) {}
    ~X() {  
            std::cout << "destructor called, cnt=" << cnt << std::endl;
            if ( cnt-- > 0 )
                this->X::~X(); // explicit recursive call to dtor
    }
};
int main()
{   
    char* buf = new char[sizeof(X)];
    X* p = new(buf) X(7);
    p->X::~X();  // explicit call to dtor
    delete[] buf;
}

My reasoning: although invoking a destructor twice is undefined behavior, per 12.4/14, what it says exactly is this:

the behavior is undefined if the destructor is invoked for an object whose lifetime has ended

Which does not seem to prohibit recursive calls. While the destructor for an object is executing, the object's lifetime has not yet ended, thus it's not UB to invoke the destructor again. On the other hand, 12.4/6 says:

After executing the body [...] a destructor for class X calls the destructors for X's direct members, the destructors for X's direct base classes [...]

which means that after the return from a recursive invocation of a destructor, all member and base class destructors will have been called, and calling them again when returning to the previous level of recursion would be UB. Therefore, a class with no base and only POD members can have a recursive destructor without UB. Am I right?

Answer

James McNellis picture James McNellis · Jun 17, 2010

The answer is no, because of the definition of "lifetime" in §3.8/1:

The lifetime of an object of type T ends when:

— if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or

— the storage which the object occupies is reused or released.

As soon as the destructor is called (the first time), the lifetime of the object has ended. Thus, if you call the destructor for the object from within the destructor, the behavior is undefined, per §12.4/6:

the behavior is undefined if the destructor is invoked for an object whose lifetime has ended