digitalmars.D - Strange copying of a constant array of structures
- Vindex (65/65) Jun 14 Last night I encountered some strange behavior of the dup
- user1234 (6/11) Jun 14 `const` is transitive, not only `S` instances are but also their
- Vindex (3/8) Jun 14 I'm guessing it's the implicit `opAssign` method after all. The
- vit (12/39) Jun 14 This is declaration of dup:
- Vindex (3/3) Jun 14 Thanks!
- Steven Schveighoffer (20/83) Jun 15 I think the fact that `dup` is not using the copy ctor is a bug.
- Vindex (3/9) Jun 16 Thank you! Especially for information about bug report.
Last night I encountered some strange behavior of the dup function. ``` import std.stdio; struct S { int x; int y; int[] arr; this(ref return scope const S rhs) { writeln("copy ctor"); this.x = rhs.x; this.y = rhs.y; this.arr = rhs.arr.dup; } } void main() { const S[] array = [S(0, 0), S(1, 2)]; S[] copy = array.dup; // error } ``` We have an issue: ``` Error: none of the overloads of template `object.dup` are callable using argument types `!()(const(S[]))` ``` But(!) if we remove the dynamic array field from the structure, everything works. I decided to get around the problem by writing my own function to copy arrays: ``` T[] copyArray(T)(const T[] arr) { T[] copy = new T[arr.length]; for (size_t i = 0; i < arr.length; i++) { copy[i] = arr[i]; // error } return copy; } void main() { const S[] array = [S(0, 0), S(1, 2)]; S[] copy = copyArray(array); } ``` Nice, simple function, but it doesn't compile on the assignment line: ``` Error: cannot implicitly convert expression `arr[i]` of type `const(S)` to `S` ``` (The feature is the same: if we remove the dynamic array field from the structure, everything works.) An additional variable solution worked: ``` T[] copyArray(T)(const T[] arr) { T[] copy = new T[arr.length]; for (size_t i = 0; i < arr.length; i++) { T elem = arr[i]; // copy ctor is called copy[i] = elem; // copy ctor isn't called! } return copy; } ``` I feel that I do not understand something, please explain what is the problem of constant structures with reference fields? And why is the copy constructor only called once in the last example? Optimization?
Jun 14
On Friday, 14 June 2024 at 08:03:47 UTC, Vindex wrote:Last night I encountered some strange behavior of the dup [...] I feel that I do not understand something, please explain what is the problem of constant structures with reference fields?`const` is transitive, not only `S` instances are but also their members.And why is the copy constructor only called once in the last example? Optimization?Yes kind of optim. The compiler is allowed to use "move-semantics". Looks like it's what happens here (at first glance).
Jun 14
I'm guessing it's the implicit `opAssign` method after all. The objects in the copy have already been created and initialized as T.init, so the copy constructor will not be called in any way.And why is the copy constructor only called once in the last example? Optimization?Yes kind of optim. The compiler is allowed to use "move-semantics". Looks like it's what happens here (at first glance).
Jun 14
On Friday, 14 June 2024 at 08:03:47 UTC, Vindex wrote:Last night I encountered some strange behavior of the dup function. ``` import std.stdio; struct S { int x; int y; int[] arr; this(ref return scope const S rhs) { writeln("copy ctor"); this.x = rhs.x; this.y = rhs.y; this.arr = rhs.arr.dup; } } void main() { const S[] array = [S(0, 0), S(1, 2)]; S[] copy = array.dup; // error } ``` We have an issue: ``` Error: none of the overloads of template `object.dup` are callable using argument types `!()(const(S[]))` ``` But(!) if we remove the dynamic array field from the structure, everything works.This is declaration of dup: ```` property T[] dup(T)(const(T)[] a) if (is(const(T) : T)) { import core.internal.array.duplication : _dup; return _dup!(const(T), T)(a); } ```` constraint is(const(T) : T) ignore copy ctors. If S has property arr then const(S) is not implicitly convertable to mutable S.
Jun 14
Thanks! So the array copy function implementation I showed is the best option?
Jun 14
On Friday, 14 June 2024 at 08:03:47 UTC, Vindex wrote:Last night I encountered some strange behavior of the dup function. ``` import std.stdio; struct S { int x; int y; int[] arr; this(ref return scope const S rhs) { writeln("copy ctor"); this.x = rhs.x; this.y = rhs.y; this.arr = rhs.arr.dup; } } void main() { const S[] array = [S(0, 0), S(1, 2)]; S[] copy = array.dup; // error } ``` We have an issue: ``` Error: none of the overloads of template `object.dup` are callable using argument types `!()(const(S[]))` ``` But(!) if we remove the dynamic array field from the structure, everything works.I think the fact that `dup` is not using the copy ctor is a bug. This was recently reported: https://issues.dlang.org/show_bug.cgi?id=24432I decided to get around the problem by writing my own function to copy arrays: ``` T[] copyArray(T)(const T[] arr) { T[] copy = new T[arr.length]; for (size_t i = 0; i < arr.length; i++) { copy[i] = arr[i]; // error } return copy; } void main() { const S[] array = [S(0, 0), S(1, 2)]; S[] copy = copyArray(array); } ``` Nice, simple function, but it doesn't compile on the assignment line: ``` Error: cannot implicitly convert expression `arr[i]` of type `const(S)` to `S` ``` (The feature is the same: if we remove the dynamic array field from the structure, everything works.)Yes, you are *assigning*, not *constructing*. You could potentially make it work using `core.lifetime.emplace`, which treats it like a construction. In order to make this work, you need an appropriate `opAssign`.An additional variable solution worked: ``` T[] copyArray(T)(const T[] arr) { T[] copy = new T[arr.length]; for (size_t i = 0; i < arr.length; i++) { T elem = arr[i]; // copy ctor is called copy[i] = elem; // copy ctor isn't called! } return copy; } ``` I feel that I do not understand something, please explain what is the problem of constant structures with reference fields?So this is *constructing* `elem` as a non-const T. This calls the copy constructor. The assignment just does a bit-copy of the first value into the second, but since both are not const, it works fine. Construction happens on *initialization*, that is, declaring a variable and specifying an initial value. Assignment happens when assigning to an *existing* variable. What is the difference? In construction, the compiler knows that the values in the type have never been assigned a value before. So it allows certain things (e.g. assigning to an immutable value). -Steve
Jun 15
On Sunday, 16 June 2024 at 02:50:20 UTC, Steven Schveighoffer wrote:On Friday, 14 June 2024 at 08:03:47 UTC, Vindex wrote:Thank you! Especially for information about bug report.[...]I think the fact that `dup` is not using the copy ctor is a bug. This was recently reported: https://issues.dlang.org/show_bug.cgi?id=24432 [...]
Jun 16