Jump to page: 1 2 3
Thread overview
I do not understand copy constructors
Aug 12, 2021
Learner
Aug 12, 2021
Paul Backus
Aug 12, 2021
Learner
Aug 12, 2021
rikki cattermole
Aug 12, 2021
Learner
Aug 12, 2021
Tejas
Aug 12, 2021
Tejas
Aug 12, 2021
Learner
Aug 12, 2021
Tejas
Aug 12, 2021
Tejas
Aug 12, 2021
drug
Aug 12, 2021
drug
Aug 12, 2021
Paul Backus
Aug 12, 2021
drug
Aug 12, 2021
Learner
Aug 12, 2021
Paul Backus
Aug 12, 2021
Learner
Aug 12, 2021
Learner
Aug 12, 2021
Tejas
Aug 12, 2021
Learner
Aug 12, 2021
Paul Backus
Aug 12, 2021
Learner
Aug 13, 2021
Paul Backus
Aug 13, 2021
Paul Backus
Aug 12, 2021
Ali Çehreli
Aug 12, 2021
Learner
August 12, 2021

I have a structure like, used by other structures:

struct A {
    int[] data;
    this(this) { data = data.dup; }
}

I am trying to upgrade it to use copy constructor:

struct A {
    int[] data
    this(ref return scope A rhs) { data = ths.data.dup; }
}

Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable

What is an inout copy constructor? What should I change in A?

August 12, 2021

On Thursday, 12 August 2021 at 08:42:27 UTC, Learner wrote:

>
struct A {
    int[] data
    this(ref return scope A rhs) { data = ths.data.dup; }
}

Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable

What is an inout copy constructor? What should I change in A?

When the compiler generates a copy constructor for a struct, it generates it with the following signature:

this(ref return scope inout(typeof(this)) src) inout

(Source: https://dlang.org/spec/struct.html#implicit-copy-constructors)

Notice that both the src object and the object being constructed (this) are qualified with inout.

inout is a special type qualifier that allows the same function to be used for mutable, const, and immutable arguments. To make this work, the compiler imposes heavy restrictions on what you can do with an inout-qualified object--only operations that are valid on mutable, const, and immutable objects are allowed for inout.

(Source: https://dlang.org/spec/function.html#inout-functions)

A's copy constructor does not have any type qualifiers on its rhs argument or its this reference, so both default to mutable. In other words: A's copy constructor can only be used to construct a mutable copy from a mutable original object. It cannot be used to construct an inout copy from an inout object.

In order to make the generated copy constructor work, you need to give A a copy constructor that can copy inout objects. There are two possibilities here:

  1. Make A's copy constructor inout: this(ref return scope inout A rhs) inout
  2. Make A's copy constructor const: this(ref return scope const A rhs) const
August 12, 2021

On Thursday, 12 August 2021 at 09:14:02 UTC, Paul Backus wrote:

>

On Thursday, 12 August 2021 at 08:42:27 UTC, Learner wrote:

>
struct A {
    int[] data
    this(ref return scope A rhs) { data = ths.data.dup; }
}

Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable

What is an inout copy constructor? What should I change in A?

When the compiler generates a copy constructor for a struct, it generates it with the following signature:

this(ref return scope inout(typeof(this)) src) inout

(Source: https://dlang.org/spec/struct.html#implicit-copy-constructors)

Notice that both the src object and the object being constructed (this) are qualified with inout.

inout is a special type qualifier that allows the same function to be used for mutable, const, and immutable arguments. To make this work, the compiler imposes heavy restrictions on what you can do with an inout-qualified object--only operations that are valid on mutable, const, and immutable objects are allowed for inout.

(Source: https://dlang.org/spec/function.html#inout-functions)

A's copy constructor does not have any type qualifiers on its rhs argument or its this reference, so both default to mutable. In other words: A's copy constructor can only be used to construct a mutable copy from a mutable original object. It cannot be used to construct an inout copy from an inout object.

In order to make the generated copy constructor work, you need to give A a copy constructor that can copy inout objects. There are two possibilities here:

  1. Make A's copy constructor inout: this(ref return scope inout A rhs) inout
  2. Make A's copy constructor const: this(ref return scope const A rhs) const

While option 2. is not working:

struct A {
    int[] data;
    this(ref return scope const A rhs) const {}
}
Error: Generating an `inout` copy constructor for `struct A` failed, therefore instances of it are uncopyable

Option .1 actually works, with an empty body, while it fails with the actual body:

struct A {
    int[] data;
    this(ref return scope inout A rhs) inout { data = rhs.data.dup; }
}
Error: cannot implicitly convert expression `dup(cast(const(int)[])rhs.data)` of type `int[]` to `inout(int[])`

It seems that there is no easy way to transition from a postblit to a copy constructor, no?

August 12, 2021
On 12/08/2021 9:36 PM, Learner wrote:
> It seems that there is no easy way to transition from a postblit to a copy constructor, no?

struct Foo {
	this(ref Foo other) {
		foreach(i, v; other.tupleof)
			this.tupleof[i] = v;
	}

	@disable this(this);
}
August 12, 2021
12.08.2021 12:36, Learner пишет:

>  > It seems that there is no easy way to transition from a postblit to a
> copy constructor, no?
> 
> 
> 

You just need both const and mutable copy ctors to replace inout one:
```D
struct A {
    int[] data;
    this(ref return scope A rhs) { data = rhs.data.dup; }
    this(ref return scope const A rhs) const { data = rhs.data.dup; }
}
```

the mutable copy ctor accepts mutable data and the const copy ctor accepts const and immutable data
August 12, 2021
12.08.2021 14:07, drug пишет:
> 12.08.2021 12:36, Learner пишет:
> 
>>  > It seems that there is no easy way to transition from a postblit to a
>> copy constructor, no?
>>
>>
>>
> 
> You just need both const and mutable copy ctors to replace inout one:
> ```D
> struct A {
>      int[] data;
>      this(ref return scope A rhs) { data = rhs.data.dup; }
>      this(ref return scope const A rhs) const { data = rhs.data.dup; }
> }
> ```
> 
> the mutable copy ctor accepts mutable data and the const copy ctor accepts const and immutable data

but using inout ctor is easier:
```D
struct A {
    int[] data;
    this(ref return scope inout A rhs) /* no inout here */ { data = rhs.data.dup; }
}
```
The problem is that if you qualify the ctor itself then if you pass const/immutable rhs to it then the ctor is const/immutable too (like the args) and of course you cannot modify this, so the error.

To make a copy ctor you need to qualify copy ctor args as inout but the copy ctor itself shall be mutable and have no const,immutable or inout qualifier.
August 12, 2021

On Thursday, 12 August 2021 at 11:19:34 UTC, drug wrote:

>
struct A {
    int[] data;
    this(ref return scope inout A rhs) /* no inout here */ { data = rhs.data.dup; }
}

The problem is that if you qualify the ctor itself then if you pass const/immutable rhs to it then the ctor is const/immutable too (like the args) and of course you cannot modify this, so the error.

To make a copy ctor you need to qualify copy ctor args as inout but the copy ctor itself shall be mutable and have no const,immutable or inout qualifier.

This is not true. Qualifying the ctor as inout works fine: https://run.dlang.io/is/Kpzp5M

The problem in this example is that .dup always returns a mutable array, even if the array being copied is inout. The solution is to cast the copy back to the original type:

this(ref return scope inout A rhs) inout
{
    data = cast(typeof(rhs.data)) rhs.data.dup;
}
August 12, 2021
On Thursday, 12 August 2021 at 10:10:17 UTC, rikki cattermole wrote:
>
> On 12/08/2021 9:36 PM, Learner wrote:
>> It seems that there is no easy way to transition from a postblit to a copy constructor, no?
>
> struct Foo {
> 	this(ref Foo other) {
> 		foreach(i, v; other.tupleof)
> 			this.tupleof[i] = v;
> 	}
>
> 	@disable this(this);
> }

This results to:

    Generating an `inout` copy constructor for `struct A` failed, therefore instances of it are uncopyable
August 12, 2021
12.08.2021 14:32, Paul Backus пишет:
> 
> This is not true. Qualifying the ctor as `inout` works fine: https://run.dlang.io/is/Kpzp5M
> 
> The problem in this example is that `.dup` always returns a mutable array, even if the array being copied is `inout`. The solution is to cast the copy back to the original type:
> 
> ```d
> this(ref return scope inout A rhs) inout
> {
>      data = cast(typeof(rhs.data)) rhs.data.dup;
> }
> ```

Yes, it's not true. I forgot that ctors are special in contrast to regular methods and can modify the aggregate they belong to even if the ctor has const/immutable/inout qualifier.
August 12, 2021
On Thursday, 12 August 2021 at 11:07:24 UTC, drug wrote:
> 12.08.2021 12:36, Learner пишет:
>
>>  > It seems that there is no easy way to transition from a postblit to a
>> copy constructor, no?
>> 
>> 
>> 
>
> You just need both const and mutable copy ctors to replace inout one:
> ```D
> struct A {
>     int[] data;
>     this(ref return scope A rhs) { data = rhs.data.dup; }
>     this(ref return scope const A rhs) const { data = rhs.data.dup; }
> }
> ```
>
> the mutable copy ctor accepts mutable data and the const copy ctor accepts const and immutable data

That still fails:

    Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable

Also if I remove the `const` body (can I assign data if the method is const?)

```D
struct A {
     int[] data;
     this(ref return scope A rhs) { data = rhs.data.dup; }
     this(ref return scope const A rhs) const {}
 }
Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable
```


« First   ‹ Prev
1 2 3