Inability to dup/~ for const arrays of class objects

Steven Schveighoffer schveiguy at yahoo.com
Tue May 28 16:58:31 PDT 2013


On Tue, 28 May 2013 19:32:30 -0400, Peter Williams  
<pwil3058 at bigpond.net.au> wrote:

> On 28/05/13 23:41, Steven Schveighoffer wrote:
>> On Sat, 25 May 2013 23:58:39 -0400, Peter Williams
>> <pwil3058 at bigpond.net.au> wrote:
>>
>>> Is the inability to use dup and ~ with const arrays of class objects a
>>> deliberate design restriction?  I couldn't find mention of it in the
>>> specification or Andrei's book and only discovered it the hard way.
>>
>> It has to be.  There is no cdup.
>>
>> For any const(T)[] x, the type of x.dup is T[].
>
> Yes, that's why I was doing the dup.  I wanted a non const copy of the  
> array.

You can't do that for arrays that contain references (which means any  
class type).  A dup is a shallow copy of the array bits.

>>  Because this would mean
>> that you would remove const, you cannot do that.
>
> I find that dup works for const T[] when T is not a class (although I  
> haven't tried it with pointers).  This means I write two versions of my  
> code - one for classes (which does (cast(T[])).dup) and one for the rest.

It won't work for pointers, or types that contain pointers either.   
Basically, anything that contains a reference cannot be implicitly copied  
 from a const version to a non-const.

It is the same for simple variables too:

const int x = 5;
int y = x; // ok

const int *px = &x;
int *py = &y;
py = px; // error!

but:

*py = *px; // ok!

It makes logical sense that you can't duplicate arrays of these types  
while removing const also.

>> As far as I know, ~ works with const arrays of class objects.  Can you
>> give a case where it fails?
>
> Looking at my code that caused me to ask this question, I've realised  
> that I'm appending a const object onto a no const array.  Once again  
> this works for non class objects but not for classes.

Because for value types, like int, const, immutable, and mutable all  
implicitly cast to each other -- you are creating a FULL copy.  When  
copying a pointer or reference type, you have to create a "deep" copy of  
the data, or the const-ness is violated.  in D, const is transitive, which  
means if you apply const to a type, it means anything it points at is also  
const.

> I can see why this might be the case as non class objects are probably  
> copied by value where the class objects are pointers and the constness  
> applies to the items in an array as well the array itself.

In fact, const(T)[] applies ONLY to the data in the array, the array  
itself is not const.  A fully const array is:

const(T[])

Which cannot be appended to or modified.

However, a const(T[]) implicitly casts to a const(T)[].

> If this behaviour is a deliberate design decision

It is

> I'll accept that and (probably) modify my code to use (cast(T[])).dup in  
> all cases.  (At the moment, I'm using a single mixin template to handle  
> this issue and the inability to use ==, !=, < and friends with constant  
> class objects. When that problem goes away I'll do the above  
> modifications.)

This is not a good idea.  You are circumventing const.  What you want is a  
deep copy.

For example:

class Dupable
{
    this(int _x) { x = _x;}
    int x;
    pure Dupable dup() const { return new Dupable(x);}
}

T[] deepDup(T)(const(T)[] n) // should put template constraint here...
{
    T[] result = new T[n.length];
    foreach(i, ref x; n)
       result[i] = x.dup();

    return result;
}

Some people have also written libraries that make deep copies without  
having to have the class support it (e.g. for serialization).  Those may  
be of use to you.

In all likelihood, the problem could be that you are using classes and  
really should be using structs.

-Steve


More information about the Digitalmars-d mailing list