2016-08-05 99 views
2

Kann man struct copy constructor explizit aufrufen, wie in C++? Kann ich etwas in der Art schreiben:D struct copy constructor

struct foo { 
    void bar() {} 
} 

foo f; 
foo(f).bar(); 

Oder muss ich bei einigen Variablen immer einen neuen Wert zuweisen?

Antwort

6

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.

0

D nicht über Kopierkonstruktoren per se, aber man konnte den impliziten Konstruktor mit dem Inhalt des bestehenden rufen mit

foo(f.tupleof).bar() 

(die eine flache Kopie zumindest schaffen würde) Die f.tupleof das gibt Liste von Strukturelementen in einer Form, die für die automatische Erweiterung zu einer Funktionsargumentliste geeignet ist.