Domanda

If I understand correctly postblit constructor in D starts from bitwise copy (always), then there is user-defined body of it.

But when I look at the body of postblit constructor it is very similar to C++ copy constructor, the only difference is in C++ the source is some object, when in D is this (itself).

Am I correct?

È stato utile?

Soluzione

Eh, close. I think you have a pretty good handle on it, but to spell it out:

a = b; (iff a and b are both the same type, a struct) translates into:

memcpy(&a, &b, b.sizeof); // bitwise copy
a.__postblit(); // call the postblit on the destination only (iff this(this) is defined on the type!)

So you don't have to explicitly assign any variables in the postblit (they are all copied automatically) and also cannot use it to implement move semantics (you don't have access to the source).

The place I most often use the postblit is when the struct is a pointer to another object, so I can increase the refcount:

struct S {
    SomeObject* wrapped;
    this(this) { if(wrapped) wrapped.addReference(); }
    ~this() { if(wrapped) wrapped.releaseReference(); }
}

This only works with references since otherwise you'd be incrementing a copy of the variable!

You can (but shouldn't) also use it to perform deep copies:

struct S {
   string content;
   this(this) { content = content.idup; }
}

But that's actually a bad idea since struct assignment is supposed to be universally cheap in D and deep copies aren't cheap. There's also generally no need anyway, since the garbage collector handles cases like double free where you might want this in C++.

The other case where I use it a lot in D is actually to disable it:

struct S {
   @disable this(this);
}
S a, b;
a = b; // compile error, b is not copyable

That's different than just not implementing a postblit at all, which leaves you with the automatic implementation of memcpy. This makes assignment an outright compile error, which you can use to funnel the user toward another method, for move semantics for example:

struct S {
   int* cool;
   @disable this(this);
   S release() { auto n = cool; cool = null; return S(cool); }
}

Since a=b is prohibited, we can now force the user to use the .release method when they want to reassign it which does our moving.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top