On Fri, 30 Sep 2011 09:27:31 -0400, Christophe
<[email protected]> wrote:
"Steven Schveighoffer" , dans le message (digitalmars.D:145767), a
écrit :
On Thu, 29 Sep 2011 13:26:11 -0400, Steven Schveighoffer
<[email protected]> wrote:
On Thu, 29 Sep 2011 13:06:55 -0400, Simen Kjaeraas
<[email protected]> wrote:
On Thu, 29 Sep 2011 16:54:24 +0200, Steven Schveighoffer
<[email protected]> wrote:
I just thought of an interesting way to make a logical const object
without casts. It requires a little extra storage, but works without
changes to the current compiler (and requires no casts).
[snip]
What do people think about this?
This is what I think about it:
I agree this breaks immutability, and needs to be addressed. I think
probably implicit casting of delegates (or items that containe
delegates) to immutable from strong-pure functions should be
disallowed.
But it's not the pattern I described, and uses a relatively new trick
(implicit immutable casting). I'll file a bug for this case.
http://d.puremagic.com/issues/show_bug.cgi?id=6741
Well, if the langage wants to be consistent, you should prevent implicit
casting of delegates to const, not just to immutable.
What you revealed is very interesting, but it is clearly a bug. A
delegate context pointer in a const object should be const, and the
delegate function should be const with regard to its context pointer if
you want the function to be called when the delegate is const.
Constness has to apply to delegates just like it applies to structs. The
explanation gets messy.
If const must be a part of the signature, then you have lost the typeless
aspect of the context pointer. If we expose the const (or not const)
nature of the pointer, then you lose the interoperability that delegates
provide. The beauty of delegates is you don't *have to* care what the
type of the context pointer is. That's defined by the function the
delegate represents.
It's why, for example, a function accepting a delegate does not
distinguish between a delegate literal and a delegate to a member function
of type Foo or a delegate to a member function of type const(Bar). You
must think of the context pointer as a hidden parameter to the delegate,
as defined when the delegate was created *not* when it is called. The
fact that it's actually stored with the delegate pointer is irrelevant.
Conceptually, it's not stored anywhere, it's just a parameter.
But in the case of the bug I filed, we are talking about a
compiler-sanctioned implicit transformation to immutable. We *must*
guarantee that when we allow an implicit transformation, that there are no
existing mutable copies of the data. An explicit transformation should be
allowed, because then the user has accepted responsibility.
The same is not true for const. It's *perfectly legal* to have a const
and mutable reference to an object at the same time. The only reason
transitive const exists and is necessary is to support transitive
immtuable.
Const is never guaranteed to prevent changing data on an object:
class C
{
int x;
void multiply(C other) const {other.x *= x;}
}
Is multiply guaranteed not to change the object? No:
c.multiply(c);
However, if c is immutable, it *is* guaranteed, because there's no way to
pass c as the parameter to multiply.
That is why I think the solution works -- if you allow the compiler to
enforce the rules of const and immutable, you can still do unexpected
things, but you do not break the guarantees of immutability. It's
definitely a loophople, but I think it's valid.
I guess you could also hide a mutable pointer of an immutable object
during its construction with the same trick.
But saving a mutable object as immutable requires a cast. It means
"compiler, I take responsibility for ensuring this no longer has any
mutable references". When the cast is *not* required, it's the compiler's
responsibility.
-Steve