Destructors, const structs, and opEquals

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sat Dec 4 06:23:31 PST 2010


On 12/4/10 12:42 AM, Don wrote:
> Officially, opEquals has to have the signature:
>
> struct Foo {
> bool opEquals(const ref Foo x) const {...}
> }

This is a compiler bug. For structs there should be no official 
implementation of opEquals, opCmp etc. All the compiler needs to worry 
about is to syntactically translate a == b to a.opEquals(b) and then let 
the usual language rules resolve the call.

> But this disallows comparisons with rvalues.
> eg,
>
> Foo bar() { Foo x = 1; return x; }
> Foo y=1;
> assert( y == bar() ); // doesn't compile
>
> You can get around this by declaring a non-ref opEquals.
> But this fails if Foo has a destructor.
>
> If a struct has a destructor, it cannot be const(this is bug 3606)
> ---
> struct S {
> ~this() {}
> }
>
> void main() {
> const S z;
> }
> ---
> bug.d(6): Error: destructor bug.S.~this () is not callable using
> argument types ()
> -------
> Likewise, it can't be a const parameter (this is bug 4338).
> void foo(const S a) {}
> It works to have it as a const ref parameter.
>
> Everything will work if you declare a const ~this(), but that seems a
> little nonsensical. And you cannot have both const and non-const ~this().
>
> I'm a bit uncertain as to how this is all supposed to work.
> (1) Should temporaries be allowed to be passed as 'const ref'?
> (2) If a struct has a destructor, should it be passable as a const
> parameter? And if so, should the destructor be called?

This is a delicate matter that clearly needs a solution. Pass of 
temporaries by const ref was a huge mistake of C++ that it has paid 
dearly for and required the introduction of a large complication, the 
rvalue references feature, to just undo the effects of that mistake. So 
I don't think we should allow that.

Regarding destructors, for every constructed object ever there must be a 
corresponding destructor call. One issue that has been a matter of 
debate in C++ has been the fact that any object becomes "deconstified" 
during destruction. The oddest consequence of that rule is that in C++ 
you can delete a pointer to a const object:

// C++ code
class A { ... };
void fun(const A* p) { delete p; /* fine */ }

There has been a lot of opposition. const is supposed to limit what you 
can do with that object, and the fact that you can't invoke certain 
methods or change members, but you can nuke the entire object, is quite 
nonintuitive (and leads to a lot of funny real-life comparisons such as 
"You can go out with my daughter, but no touching. Of course, you can 
shoot her if you so wish.")

In D, the rule must be inferred from D's immutability rules, which 
pretty much dictate that the destructor must be overloaded for non-const 
and const (and possibly invariant if the struct needs that).


Andrei


More information about the Digitalmars-d mailing list