Destructors, const structs, and opEquals

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sat Dec 4 21:42:20 PST 2010


On 12/4/10 22:42 CST, Steven Schveighoffer wrote:
> 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.

I don't think so. On the contrary, declaring with inout is the most 
adaptive.

>> [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.

Yah, still not getting the original point there.


Andrei


More information about the Digitalmars-d mailing list