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

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

Reply via email to