Error: cannot implicitly convert expression (this) of type const(S) to S

Jonathan M Davis jmdavisProg at gmx.com
Sat Sep 18 14:20:31 PDT 2010


On Saturday 18 September 2010 09:58:15 Steven Schveighoffer wrote:
> On Sat, 18 Sep 2010 05:15:38 -0400, Jonathan M Davis <jmdavisProg at gmx.com>
> 
> wrote:
> > Okay, if I try and compile the following program.
> > 
> > struct S
> > {
> > 
> >     @property S save() const
> >     {
> >     
> >         return this;
> >     
> >     }
> >     
> >     int[] _val;
> > 
> > }
> > 
> > void main()
> > {
> > }
> > 
> > 
> > I get the error message
> > 
> > d.d(5): Error: cannot implicitly convert expression (this) of type
> > const(S) to S
> 
> Yes, because you are converting "this" from a const(S) to an S to return
> it.
> 
> Try:
> 
> @property const(S) save() const
> {
>     return this;
> }
> 
> > If I remove const from save(), then it works, but with const there, it
> > doesn't.
> > If I change _val to int or a struct type which does not contain an
> > array, it
> > works. If I change _val to a struct type which has a member variable
> > which is an
> > array, it doesn't work.
> 
> Because arrays are reference types.
> 
> You can implicitly convert a const(int) to an int because it's not a
> reference type.  But you can't implicitly convert a const(int *) to an int
> * because it's a reference type.
> 
> That axiom of implicit conversion is propagated to structs as well -- if a
> struct has only value types, then it can be implicitly converted,
> reference types cannot.
> 
> I'll give you an example of why your version should not work.  Let's say
> it *did* compile, then this function is allowed to violate const:
> 
> void foo(const(S) s)
> {
>     S s2 = s.save;
>     s2._val[0]++;
>     assert(s2._val[0] == s._val[0]); // oops, I modified s._val[0], but s
> is const!
> }
> 
> > From the looks of it, there's something about having an array as a member
> > variable which makes this blow up. Perhaps it has to do with the fact
> > that _val
> > is a reference type and would be shared? Do I need to declare a postplit
> > constructor to fix this? Declaring one doesn't seem to help, but maybe I
> > just
> > don't know how to declare them correctly.
> 
> In reality, you cannot make save const, unless you want to do a deep copy
> (but I recommend against that, save should be a quick operation).

Well, I was trying to make a deep copy (that's what it does if it doesn't have 
an array in it anyway). I suppose whether it really needs to be const or not 
depends on what you're trying to do with it. I'm definitely trying to create a 
range here, and if save() is supposed to make a copy of the range, whether that 
needs to be a shallow copy or a deep copy depends on what the std.algorithm stuff 
does with it. When I think copy, I usually think deep copy, but that's not 
necessarily the case. I will have to enquire as to the intent of save(). For 
value types, shallow copy and deep copy are the same, so it's not an issue. But 
for class ranges or struct ranges with references, it does become an issue.

> 
> The solution is actually inout.  With inout, you can assert that save does
> not modify the data, but that it doesn't alter the constancy of the actual
> type.
> 
> @property inout(S) save() inout
> {
>    return this;
> }
> 
> Should work for S, const(S) and immutable(S).
> 
> However, inout doesn't work at all right now.  Please vote for bug
> http://d.puremagic.com/issues/show_bug.cgi?id=3748 (oh wait, you already
> did, but others should too :)
> 
> -Steve

I would _love_ for all of the const-related issues to be fixed, and I most 
definitely voted on at least the major ones. Heck, when I started using D back 
with 2.017 or so, I chose D2 over D1 because D2 had const and D1 didn't.


More information about the Digitalmars-d-learn mailing list