Thread overview
Why does this call the copy constructor 2 times and the assigment constructor once?
Nov 19, 2021
rempas
Nov 19, 2021
Paul Backus
Nov 19, 2021
rempas
November 19, 2021

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.

November 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:

// 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:

// 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 and auto ref parameters in the D language specification.

November 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!