Nun, technisch gesehen hat D nicht einmal Kopierkonstrukteure. Stattdessen können Strukturen PostBlit-Konstruktoren haben. z.B.
Im Allgemeinen versucht D Strukturen so weit wie möglich zu verschieben und nicht zu kopieren. Und wenn es sie kopiert, tut es eine bitweise Kopie der Struktur und führt dann den postblit-Konstruktor aus (falls es einen gibt), um die Struktur nach der Tatsache zu mutieren, um Sachen zu tun, die über eine bitweise Kopie hinaus getan werden müssen - z. wenn Sie eine tiefe Kopie eines Mitglieds wollen
struct S
{
this(this)
{
if(i !is null)
i = new int(*i);
}
int* i;
}
Eine Kopie Konstruktor (in C++), auf der anderen Seite, erstellt eine neue Struktur/Klasse und initialisiert jedes Mitglied mit einer Kopie des entsprechenden Elements in der Struktur/Klasse, die kopiert wird - oder mit dem, was in der Initialisierungsliste des Kopierkonstruktors initialisiert wird. Es kopiert nicht und mutiert dann, wie es bei D's postblit-Konstruktor passiert. Ein Copy-Konstruktor und ein Postblit-Konstruktor unterscheiden sich also subtil.
Einer der Nebenwirkungen davon ist, dass alle Strukturen/Klasse in C++ Kopierkonstruktoren haben (der Compiler erzeugt immer einen für Sie, wenn Sie keinen deklarieren), nicht alle Strukturen in D haben postblit Konstruktoren. Tatsächlich tun die meisten nicht. Der Compiler wird einen erzeugen, wenn die Struktur eine andere Struktur enthält, die einen postblit-Konstruktor hat, aber ansonsten wird sie keinen erzeugen, und das Kopieren wird nur eine bitweise Kopie machen. Und wenn es kein postblit Konstrukt gibt, können Sie es nicht implizit oder explizit nennen.
Wenn wir nun diese kompilieren
struct A
{
}
pragma(msg, "A: " ~ __traits(allMembers, A).stringof);
druckt
A: tuple()
A
hat keine Mitglieder - sei es Membervariablen oder Funktionen. Keine wurde deklariert und der Compiler hat keine generiert.
struct B
{
A a;
string s;
}
pragma(msg, "B: " ~ __traits(allMembers, B).stringof);
druckt
B: tuple("a", "s")
Es hat zwei Mitglieder - die explizit deklariert Membervariablen. Es hat auch keine Funktionen. Das Deklarieren von Elementvariablen ist kein Grund dafür, dass der Compiler Funktionen generiert.Wenn wir jedoch
struct C
{
this(this)
{
import std.stdio;
writeln("C's postblit");
}
int i;
string s;
}
pragma(msg, "C: " ~ __traits(allMembers, C).stringof);
druckt
C: tuple("__postblit", "i", "s", "__xpostblit", "opAssign")
Nicht nur sind die beiden Elementvariablen aufgeführt, aber es hat auch __postblit
(das ist die explizit deklariert postblit Konstruktor) sowie __xpostblit
und opAssign
kompilieren . __xpostblit
ist der postblit-Konstruktor, der vom Compiler erzeugt wird (mehr dazu in einer Sekunde), und opAssign
ist der Zuweisungsoperator, den der Compiler erzeugte (was benötigt wird, weil C
einen postblit-Konstruktor hat).
struct D
{
C[5] sa;
}
pragma(msg, "D: " ~ __traits(allMembers, D).stringof);
druckt
D: tuple("sa", "__xpostblit", "opAssign")
Beachten Sie, dass es __xpostblit
aber nicht __postblit
hat. Das liegt daran, dass es keinen explizit erklärten postblit-Konstruktor gibt. __xpostblit
wurde generiert, um den postblit-Konstruktor jeder Elementvariable aufzurufen. sa
ist ein statisches Array von C
und C
hat einen postblit Konstruktor. Um sa
richtig zu kopieren, muss der PostBlit-Konstruktor für C
für jedes der Elemente in sa
aufgerufen werden. 's __xpostblit
macht das. C
hat auch __xpostblit
, aber es hat keine Mitglieder mit postblit Konstruktoren, so __xposblit
ruft nur __postblit
.
struct E
{
this(this)
{
import std.stdio;
writeln("E's postblit");
}
C c;
}
pragma(msg, "E: " ~ __traits(allMembers, E).stringof);
druckt
E: tuple("__postblit", "c", "__xpostblit", "opAssign")
So E
- wie C
- hat sowohl __postblit
und __xpostblit
. __postblit
ist der explizite postblit-Konstruktor, und __xpostblit
ist derjenige, der vom Compiler generiert wird. In diesem Fall hat die Struktur jedoch Membervariablen mit einem postblit-Konstruktor, so dass __xpostblit
mehr zu tun hat als nur __postblit
.
Wenn Sie
void main()
{
import std.stdio;
C c;
writeln("__posblit:");
c.__postblit();
writeln("__xposblit:");
c.__xpostblit();
}
hätte es
__posblit:
C's postblit
__xposblit:
C's postblit
drucken Es gibt also keinen wirklichen Unterschied zwischen den beiden, während, wenn Sie
hatte
void main()
{
import std.stdio;
D d;
writeln("__xposblit:");
d.__xpostblit();
}
ausdrucken würde
__xposblit:
C's postblit
C's postblit
C's postblit
C's postblit
C's postblit
Beachten Sie, dass C
'postblit wird 5 mal aufgerufen - einmal für jedes Element in D
' s Mitglied, sa
. Und wir konnten nicht __postblit
auf aufrufen, weil es keinen expliziten postblit Konstruktor hat - nur der implizite.
void main()
{
import std.stdio;
E e;
writeln("__posblit:");
e.__postblit();
writeln("__xposblit:");
e.__xpostblit();
}
würde
__posblit:
E's postblit
__xposblit:
C's postblit
E's postblit
Und in diesem Fall drucken, können wir sehen, dass __postblit
und __xpostblit
unterschiedlich sind. Der Aufruf __postblit
ruft nur den explizit deklarierten postblit-Konstruktor auf, während er __xpostblit
und die postblit-Konstruktoren der Elementvariablen aufruft.
Und natürlich, da A
und B
nicht posblit Bauer und keine Mitglieder haben, die sie haben, wäre es illegal, entweder __postblit
oder __xpostblit
auf sie zu nennen.
Also, ja, Sie können den postblit Konstruktor explizit aufrufen - aber nur wenn es einen hat, und Sie sollten es fast sicher nicht aufrufen. Wenn eine Funktion mit __
beginnt oder einer der überladenen Operatoren ist (und daher mit op
beginnt), sollte sie fast nie explizit aufgerufen werden - und das schließt den postblit-Konstruktor ein. Aber wenn Sie einen legitimen Grund finden, es anzurufen, denken Sie daran, dass Sie wahrscheinlich __xpostblit
statt __postblit
anrufen möchten, ansonsten wird der Postblit für die Mitgliedsvariablen nicht ausgeführt. Sie können dafür testen, indem Sie __traits(hasMember, S1, "__xpostblit")
oder mit dem schlecht benannten hasElaborateCopyConstructor
von Std.Traits (der meiste Code sollte hasElaborateCopyConstructor
verwenden, da es mehr idiomatische ist). Wenn Sie aus irgendeinem Grund __postblit
anrufen möchten, müssen Sie dafür mit __traits
statt std.traits testen, denn so ziemlich nichts außerhalb druntime kümmert sich, ob ein Typ __postblit
deklariert. Das Zeug, das sich um posblit Konstrukteure kümmert, kümmert sich um __xpostblit
, da das existieren kann, ob __postblit
deklariert wurde oder nicht.