www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why does this call the copy constructor 2 times and the assigment

reply rempas <rempas tutanota.com> writes:
Hi! I'm trying to make my own string type for my library. Anyway, 
I'm not very experienced with structs/classes so I don't 
understand what's going one here. I will not post the full code 
because I don't think that anyone would like it so I will just 
post the important parts that play a role (tho in any case feel 
free to ask for the full code).

Code:
```
import core.memory;

import core.stdc.stdio;
import core.stdc.string;
import core.stdc.stdlib;

struct str {
private:
   char* _val;
   uint* _count;
   ulong _cap, _len;

public:
   // Constructors
   this(const char* val) {
     printf("Debug: Called this(const char* val)\n");
     this._val = cast(char*)val;
     this._count = cast(uint*)pureMalloc(4);
     *this._count = 0;
     this._cap = 0;
     this._len = strlen(val);
   }

   // Copy constructor
   this(ref return scope str rhs) {
     printf("Debug: Copy constructor called!!! (strig rhs)\n");
     this._cap = rhs.length;
     this._len = this._cap;
     this._val = cast(char*)pureMalloc(this._len);
     strcpy(this._val, rhs.ptr);
     this._count = cast(uint*)pureMalloc(4);
     *this._count = 1;
   }

   // Assigment constructors
   str opAssign(str rhs) {
     printf("Debug: Assigment constructor called!!! (str rhs)\n");
     if (*this._count == 1) {
       free(this._val);
     } else if (*this._count > 1) {
       (*this._count)--;
     } else *this._count = 1;

     this._val = cast(char*)pureMalloc(rhs.length);
     if (!this._val) {
       fprintf(stderr, "Could not allocate memory for the str 
object");
       exit(1);
     }

     strcpy(this._val, rhs.ptr);
     this._cap = rhs.length;
     this._len = rhs.length;
     return this;
   }

    property char* ptr() { return _val; }
    property ulong length() { return _len; }
}

extern (C) int main() {
   str name = "Mike";
   str other_name = "Anna";
   other_name = name;
   return 0;
}
```

So, when I assign the value of the variable "name" in the 
"other_name", first it will call the copy constructor, then it 
will call the assignment constructor and then it will call the 
copy constructor again. Why is this happening? I was expecting 
only the assignment constructor to get called.
Nov 19 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 19 November 2021 at 14:05:40 UTC, rempas wrote:
 So, when I assign the value of the variable "name" in the 
 "other_name", first it will call the copy constructor, then it 
 will call the assignment constructor and then it will call the 
 copy constructor again. Why is this happening? I was expecting 
 only the assignment constructor to get called.
When you pass a struct instance to a function by value, or return a struct instance from a function by value, a copy is made, and the copy constructor is called. Your `opAssign` takes `rhs` by value, and returns a `str` by value: ```d // Returned by value // | // v str opAssign(str rhs) { // ^ // | // Passed by value ``` So, every call to it will result in two calls to `str`'s copy constructor. If you want to avoid this, you can change `opAssign` to have the following signature: ```d // Returned by referece // | // v ref str opAssign()(auto ref str rhs) // ^ // | // Passed by reference (if possible) ``` Since `auto ref` is only allowed for template functions, I have added an empty template argument list (the `()`) to make `opAssign` into a function template. You can read more about [`ref` functions][1] and [`auto ref` parameters][2] in the D language specification. [1]: https://dlang.org/spec/function.html#ref-functions [2]: https://dlang.org/spec/template.html#auto-ref-parameters
Nov 19 2021
parent rempas <rempas tutanota.com> writes:
On Friday, 19 November 2021 at 14:22:07 UTC, Paul Backus wrote:
 When you pass a struct instance to a function by value, or 
 return a struct instance from a function by value, a copy is 
 made, and the copy constructor is called.

 Your `opAssign` takes `rhs` by value, and returns a `str` by

 [...]

 Since `auto ref` is only allowed for template functions, I have 
 added an empty template argument list (the `()`) to make 
 `opAssign` into a function template.

 [...]
Interesting! It's weird that it works like that and explicitly calls a constructor but it indeed works as expected now. Thanks a lot and have an amazing day!
Nov 19 2021