digitalmars.D.learn - Why does this call the copy constructor 2 times and the assigment
- rempas (71/71) Nov 19 2021 Hi! I'm trying to make my own string type for my library. Anyway,
- Paul Backus (35/40) Nov 19 2021 When you pass a struct instance to a function by value, or return
- rempas (4/13) Nov 19 2021 Interesting! It's weird that it works like that and explicitly
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
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
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