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