Destructors, const structs, and opEquals

Steven Schveighoffer schveiguy at yahoo.com
Sat Dec 4 20:42:31 PST 2010


On Sat, 04 Dec 2010 15:58:43 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> On 12/4/10 2:39 PM, Don wrote:
>> Andrei Alexandrescu wrote:
>>> On 12/4/10 9:23 AM, Don wrote:
>>>> Andrei Alexandrescu wrote:
>>>>> 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.
>>>>
>>>> Fine, but try to implement a generic type which supports ==.
>>>> For example, Tuple in std.typecons.
>>>> The only reason that many of the Tuple unit tests pass is that  
>>>> opEquals
>>>> never gets instantiated.
>>>
>>> I think it should be as follows:
>>>
>>> bool opEquals(auto ref inout Tuple rhs) inout {
>>> foreach (i, T; Types) {
>>> if (this[i] != rhs[i]) return false;
>>> }
>>> return true;
>>> }
>>>
>>> It looks a bit alembicated but let's not forget that Tuple is supposed
>>> to be very flexible and to do a lot of things.
>>
>> Ouch.
>>
>> The semantics of == are very well defined, and simple. Always, you want
>> read-only access to the two objects, in the fastest possible way.
>> I don't see why the complexity of the object should have any influence
>> on the signature of ==. If there's a method which works correctly and
>> efficiently in every case, why isn't it the only way?
>
> It's not the complexity of the object as much as "don't pay for const if  
> you don't use it". If Tuple's opEquals is implemented as above, it works  
> with code that doesn't use const at all. Tack a const on to it,  
> everybody must define opEquals with const.

You have not addressed that problem -- tack an inout on it, everybody must  
define opEquals with inout.

> [snip]
>>>> Has this sort of idea been explored? Is there something wrong with it?
>>>
>>> What's wrong with it is it consistently leads to suboptimal code,
>>> which has created a hecatomb of problems for C++ (even the very
>>> carefully conceived rvalue references feature, for all its size and
>>> might, is unable to fix them all).
>>>
>>> The caller should create the copy and pass the responsibility of
>>> destroying to the callee. This is because oftentimes the actual object
>>> destroyed is not the same object that was constructed. You see those
>>> trailing calls ~__tmp1, ~__tmp2 at the end of your code? They are a
>>> tin cat stuck to code's tail.
>>
>> They need not apply to functions with 'inout' parameters. A parameter
>> which is passed by 'inout' will either be used in the return value, or
>> it will need to be destroyed.
>> It seems clear to me that when you declare an 'inout' parameter, you're
>> assuming responsibility for the lifetime of the object.
>
> I'm not sure I understand, sorry. To recap, "inout" used to mean "ref"  
> but not anymore. It just means "this stands for either const, immutable,  
> or nothing". I'm not sure how that affects caller's responsibility.

It does not stand for const, immutable, or nothing exactly.  It binds the  
constancy of the output with the constancy of the inputs in an enforceable  
way.  It imposes a temporary const on everything, and then returns things  
back to the way they were, even though you are returning a portion of a  
parameter.

-Steve


More information about the Digitalmars-d mailing list