logical const without casts!
Steven Schveighoffer
schveiguy at yahoo.com
Fri Sep 30 07:01:28 PDT 2011
On Fri, 30 Sep 2011 09:27:31 -0400, Christophe
<travert at phare.normalesup.org> wrote:
> "Steven Schveighoffer" , dans le message (digitalmars.D:145767), a
> écrit :
>> On Thu, 29 Sep 2011 13:26:11 -0400, Steven Schveighoffer
>> <schveiguy at yahoo.com> wrote:
>>
>>> On Thu, 29 Sep 2011 13:06:55 -0400, Simen Kjaeraas
>>> <simen.kjaras at gmail.com> wrote:
>>>
>>>> On Thu, 29 Sep 2011 16:54:24 +0200, Steven Schveighoffer
>>>> <schveiguy at yahoo.com> 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
More information about the Digitalmars-d
mailing list