Thread overview
Copying reference types by value
Apr 17, 2013
Ali Çehreli
Apr 17, 2013
Ali Çehreli
Apr 19, 2013
Ali Çehreli
April 17, 2013
Hello all,

Suppose I have a couple of variables a, b that are reference types (e.g. arrays
or classes).  Then, setting a = b will mean that the two reference the same entity.

If they're arrays, one can copy the values readily by setting a[] = b[], but this isn't a generic solution -- suppose instead of arrays, I a and b are instances of

	class A {
		int one;
		double two;
		size_t three;

		this(int _one, double _two, size_t _three)
		{
			one = _one;
			two = _two;
			three = _three;
		}
	}

Now, I can see several solutions.  The first is just to define a generic copy function, like,

	void copyMyClass(const A from, ref A to) { ... }

... which copies over values.  However, as a general solution that seems a bit dodgy as it presumes I have the right to read and write all internal values of the class.

Defining a .dup method doesn't seem appropriate either because although it allows me to duplicate the values, setting

	a = b.dup;

will mean that a now becomes a new object, and any other entities that were pointing at the original a will suddenly become decoupled from its values.  So, if I do,

	c = a;
	a = b.dup;

... I will no longer have c equal to a.

So, can anyone recommend an appropriate general method for copying the values of one reference type instance to another?  Perhaps an appropriate operator to overload?

Thanks & best wishes,

    -- Joe
April 17, 2013
On 04/17/2013 09:17 AM, Joseph Rushton Wakeling wrote:

> Defining a .dup method doesn't seem appropriate either because although it
> allows me to duplicate the values, setting
>
> 	a = b.dup;
>
> will mean that a now becomes a new object, and any other entities that were
> pointing at the original a will suddenly become decoupled from its values.  So,
> if I do,
>
> 	c = a;
> 	a = b.dup;
>
> ... I will no longer have c equal to a.

I haven't used it anywhere but I did think about this very issue just the other day. I thought about defining a takeOver() member function that does what you need:

  // take over the members of b
  a.takeOver(b); // b becomes null (or .init?)

Using takeOver() along with dup(), one can copy state:

  a.takeOver(b.dup());

Of course there can be a wrapper function for that. :)

But I am curious as well. What do others do in such cases? Are there accepted solutions in other languages like Java and C#?

Ali

April 17, 2013
On 04/17/2013 06:54 PM, Ali Çehreli wrote:
> I haven't used it anywhere but I did think about this very issue just the other day. I thought about defining a takeOver() member function that does what you need:
> 
>   // take over the members of b
>   a.takeOver(b); // b becomes null (or .init?)

Ahh, so you mean that the current members of a get discarded and replaced with those of b?

> Using takeOver() along with dup(), one can copy state:
> 
>   a.takeOver(b.dup());
> 
> Of course there can be a wrapper function for that. :)

It's an intriguing thought.  How do you get round the issue of private variables ... ?  And what's the benefit over a method like, say,

	a.copyFrom(b);

... which just copies values between corresponding variables?  (Leaving aside that takeOver has applications of its own -- I mean my question purely in the context of copying values.)
April 17, 2013
On 04/17/2013 10:09 AM, Joseph Rushton Wakeling wrote:

> On 04/17/2013 06:54 PM, Ali Çehreli wrote:
>> I haven't used it anywhere but I did think about this very issue just the other
>> day. I thought about defining a takeOver() member function that does what you need:
>>
>>    // take over the members of b
>>    a.takeOver(b); // b becomes null (or .init?)
>
> Ahh, so you mean that the current members of a get discarded and replaced with
> those of b?

Yes but as I said I am not sure how useful or needed this whole thing is.

The language handles copy, move, and assignment for structs. takeOver() seems to be a primitive operation for classes.

>> Using takeOver() along with dup(), one can copy state:
>>
>>    a.takeOver(b.dup());
>>
>> Of course there can be a wrapper function for that. :)
>
> It's an intriguing thought.  How do you get round the issue of private variables
> ... ?

takeOver()'s parameter is presumably the same type as the class's so the private members are accessible. Of course any member may be well encapsulated; then, takeOver() depends on similar functionality that those members provide, perhaps through member functions that are again named takeOver() but there is no standard for it.

A framework may require that such "take over" member functions should be marked by a specific UDA.

>  And what's the benefit over a method like, say,
>
> 	a.copyFrom(b);

That's what I meant by "a wrapper function." But I think move is a more fundamental operation that elides a copy. If we had only copyFrom(), then the following would indeed need to copy:

    // foo() returns a class object
    a.copyFrom(foo());

On the other hand, takeOver() would have the ability to move the members of the returned rvalue object without needing to copy.

> ... which just copies values between corresponding variables? (Leaving aside
> that takeOver has applications of its own -- I mean my question purely in the
> context of copying values.)

Agreed.

Ali

April 18, 2013
On 04/17/2013 10:29 PM, Ali Çehreli wrote:
> Yes but as I said I am not sure how useful or needed this whole thing is.
> 
> The language handles copy, move, and assignment for structs. takeOver() seems to be a primitive operation for classes.

Could you expand a bit on that?  Just that I'm not sure I understand your meaning completely, and I'm curious. :-)

> That's what I meant by "a wrapper function." But I think move is a more fundamental operation that elides a copy. If we had only copyFrom(), then the following would indeed need to copy:
> 
>     // foo() returns a class object
>     a.copyFrom(foo());
> 
> On the other hand, takeOver() would have the ability to move the members of the returned rvalue object without needing to copy.

OK, clear.  In fact seems possible that copying vs. takeover might be useful to define separately and use whichever is optimal for the given use-case.

> Agreed.

I'd assumed this problem would be some fairly straightforward aspect of the language that I just wasn't familiar with, so I'm quite struck by the fact that it actually seems a non-trivial problem.

April 19, 2013
On 04/18/2013 06:22 AM, Joseph Rushton Wakeling wrote:> On 04/17/2013 10:29 PM, Ali Çehreli wrote:
>> Yes but as I said I am not sure how useful or needed this whole thing is.
>>
>> The language handles copy, move, and assignment for structs. takeOver() seems to
>> be a primitive operation for classes.
>
> Could you expand a bit on that?  Just that I'm not sure I understand your
> meaning completely, and I'm curious. :-)

Being value types, structs enjoy automatic handling of copy, assignment, and in the case of rvalues, move.

struct S
{
    int i;
}

S makeS()
{
    return S();
}

void main()
{
    auto a = S();
    auto b = a;    // a is copied to b
    b = a;         // a is assigned to b
    b = makeS();   // the returned rvalue is 'moved' to b
}

Of course that works only for structs with simple value members. When the automatic handling is not sufficient or does the wrong thing, then the programmer must provide this(this), opAssign(), or ~this(). Things get more interesting when the members are mutable or immutable references.

With the above definition of S, all of the following compile:

    immutable(S) imm0;
    S mut = imm0;            // immutable to mutable

    immutable(S) imm1 = mut;  // mutable to immutable

    immutable(S) imm2 = imm0; // immutable to immutable

Now naively add another member to S:

struct S
{
    int i;
    int[] arr;  // <-- added
}

Only the latter of the three assignments compile:

    immutable(S) imm0;
    S mut = imm0;             // compilation ERROR

    immutable(S) imm1 = mut;  // compilation ERROR

    immutable(S) imm2 = imm0; // compiles

Anyway... I am trying to wrap up what I have learned so far before going any further. :)

For classes, none of these operations make sense: There is no automatic copying, no automatic assignment, nor there are rvalues to move.

> I'd assumed this problem would be some fairly straightforward aspect of the
> language that I just wasn't familiar with, so I'm quite struck by the fact that
> it actually seems a non-trivial problem.

My experience has been with C++ so I don't know how this is solved in Java or C#. Maybe this is not a problem with the common idioms in those languages, which would also mean that this is not a problem with D classes. Rather, if one has decided to go with a class, then value semantics were not a consideration to begin with.

Ali