On Sat, 04 Dec 2010 15:58:43 -0500, Andrei Alexandrescu <seewebsiteforem...@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

Reply via email to