TDPL: Manual invocation of destructor

Steven Schveighoffer schveiguy at yahoo.com
Mon Aug 9 13:59:20 PDT 2010


On Mon, 09 Aug 2010 16:31:39 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> Steven Schveighoffer wrote:
>> On Mon, 09 Aug 2010 15:46:27 -0400, Andrei Alexandrescu  
>> <SeeWebsiteForEmail at erdani.org> wrote:
>>
>>> Lutger wrote:
>>>> Steven Schveighoffer wrote:
>>>>
>>>>> On Mon, 09 Aug 2010 08:28:38 -0400, Andrej Mitrovic
>>>>> <andrej.mitrovich at gmail.com> wrote:
>>>>>
>>>>>> It's rather perplexing, isn't it? It states in TDPL:
>>>>>>
>>>>>> "After you invoke clear, the object is still alive and well, but its
>>>>>> destructor has been called and the object is now carrying its
>>>>>> default-constructed stated. During the next garbage collection, the
>>>>>> destructor is called again, because the garbage collector has no  
>>>>>> idea in
>>>>>> what state you have left the object."
>>>>> This seems totally wrong, what if an object has no default  
>>>>> constructor?
>>>>> The spec used to say (maybe it still does) that a destructor is  
>>>>> guaranteed
>>>>> to only ever be called once.
>>>>  The spec still does, it is not updated since it describes delete,  
>>>> not clear.  If you omit the default constructor, no constructor will  
>>>> be called. Also not for the base classes even if they have a default  
>>>> constructor. This looks like a bug.
>>>
>>> Yes, not calling the constructors of base classes is an implementation  
>>> bug. If a class does not define any constructor at all, it has a de  
>>> facto default constructor. If a class does define some constructor but  
>>> not a parameterless one, it is a bug in the implementation if clear()  
>>> compiles.
>>  How can this be decided at compile time?
>>  basic counter-example:
>>  class C
>> {
>>    this(int x) {}
>> }
>>  // how do you statically disable this?
>> void foo(Object o)
>> {
>>   clear(o);
>> }
>>  void foo(C c)
>> {
>>    auto c = new C(1);
>>    foo(c);
>> }
>
> Sorry, I got things mixed.
>
>> I always thought clear would simply overwrite the object with it's  
>> default data as defined in the TypeInfo (i.e. before a constructor is  
>> normally called).  Calling the constructor is absolutely wrong.  We  
>> don't want to reset the object, we want to free it's resources.   
>> Re-constructing it may reallocate resources *THAT WE JUST ASKED TO BE  
>> DESTROYED MANUALLY*.  To say clear as defined is a replacement for  
>> delete is complete fantasy.
>
> Well this is my view as well and the original intent of clear(). The  
> problem is, if someone defined a default constructor, they presumably  
> have a nontrivial invariant to satisfy. I'm unclear on what's the best  
> path to take.

Who cares about invariants when an object is destructed?  First,  
invariants are disabled in release mode.  Second, I find it rare that a  
class has an invariant.  Why take actions in release mode and that can run  
counter to what the programmer has requested (destruction of on object,  
not construction) to satisfy invariants that 99% of the time do not exist?

But we have to figure out how to make it so people can put in their  
invariants as desired, and also be able to use clear as desired.

A couple options I see:

1. object invariants only run when the destructor has not yet been  
called.  We probably can use some hidden bits to flag this.
2. make it known that defining an invariant on an object should pass  
before the constructor is called.

My preference is for 1, esp. since invariants are a huge drag on  
performance anyways, adding a little bit more isn't going to hurt.

I also strongly suggest the destructor only be called once.  Having a  
destructor called more than once means your destructor has to take into  
account that the object may be already destructed.  This would be helped  
by the same mechanism that would make sure invariants aren't run after  
destruction.

-Steve


More information about the Digitalmars-d mailing list