In jedem Compiler wird der syntaktische [und semantische] Analyseprozess vor dem Codeoptimierungsprozess durchgeführt.
Der Code muss syntaktisch gültig sein, sonst wird er nicht kompiliert. Es ist nur in der späteren Phase (d. H. Code-Optimierung), dass der Compiler beschließt, die temporäre, die es erstellt, zu elide.
Sie brauchen also einen zugänglichen copy-tor.
C ++ erlaubt explizit mehrere Optimierungen mit dem Copy-Konstruktor, die die Semantik des Programms tatsächlich verändern. (Dies steht im Gegensatz zu den meisten Optimierungen, die die Semantik des Programms nicht beeinflussen). Insbesondere gibt es mehrere Fälle, in denen der Compiler ein vorhandenes Objekt erneut verwenden darf, anstatt es zu kopieren, wenn es weiß, dass das vorhandene Objekt nicht mehr erreichbar ist. Dies (Kopieraufbau) ist ein solcher Fall; Ein anderer ähnlicher Fall ist die "Rückgabewert-Optimierung" (RVO), wobei C ++, wenn Sie die Variable deklarieren, die den Rückgabewert einer Funktion enthält, diese auf dem Frame des Aufrufers zuweisen kann, so dass sie nicht benötigt wird um es zurück zum Aufrufer zu kopieren, wenn die Funktion beendet ist.
Im Allgemeinen spielen Sie in C ++ mit Feuer, wenn Sie einen Kopierkonstruktor definieren, der Nebenwirkungen hat oder irgendetwas anderes als nur das Kopieren tut.
RVO und NRVO, Kumpel. Tadellos guter Fall der Kopie ellision.
Fragen Sie, warum der Compiler die Zugriffskontrolle durchführt? 12.8 / 14 in C ++ 03:
Ein Programm ist schlecht formatiert, wenn die Kopie Konstruktor oder die Kopierzuordnung Operator für ein Objekt ist implizit verwendet und die spezielle Mitgliedfunktion ist nicht zugänglich
Wenn die Implementierung "den Kopieraufbau weglässt" (erlaubt bis 12.8 / 15), glaube ich nicht, dass der copy ctor nicht mehr "implizit verwendet" wird, sondern einfach nicht ausgeführt wird.
Oder fragen Sie, warum der Standard das sagt? Wenn die Eliminierung der Kopie eine Ausnahme von dieser Regel für die Zugriffsprüfung wäre, wäre Ihr Programm in Implementierungen, die die Elision erfolgreich durchführen, wohlgeformt, aber in Implementierungen, die dies nicht tun, schlecht gebildet.
Ich bin mir ziemlich sicher, dass die Autoren das für eine schlechte Sache halten würden. Natürlich ist es einfacher, portablen Code auf diese Weise zu schreiben - der Compiler sagt Ihnen, wenn Sie Code schreiben, versucht ein nicht kopierbares Objekt zu kopieren, selbst wenn die Kopie in Ihrer Implementierung nicht vorhanden ist. Ich vermute, dass es auch Implementierern unangenehm sein könnte, herauszufinden, ob die Optimierung erfolgreich sein wird bevor der Zugriff überprüft wird (oder die Zugriffsprüfung verzögert wird, nachdem die Optimierung versucht wurde), obwohl ich keine Ahnung habe, ob dies gerechtfertigt ist Rücksicht.
Könnte dieses Verhalten gefährlich sein? ich Ich meine, ich könnte etwas anderes Nützliches tun Ding in der Kopie-Ctor, aber wenn es nennt es nicht, dann nicht das Verhalten des Programms ändern?
Natürlich könnte es gefährlich sein - Seiteneffekte in Kopierkonstruktoren treten genau dann auf, wenn das Objekt tatsächlich kopiert wird, und Sie sollten es entsprechend gestalten: Der Standard sagt, dass Kopien entfernt werden können, also setzen Sie keinen Code in a Kopieren Sie den Konstruktor, es sei denn, Sie freuen sich, dass er unter den in 12.8 / 15 definierten Bedingungen veröffentlicht wird:
%Vor%Hier Sie können dies finden (mit Ihrem Kommentar;)):
[der Standard] sagt auch, dass die temporäre Kopie kann elided sein, aber die Semantik Einschränkungen (zB Zugänglichkeit) der Kopierkonstruktor muss immer noch sein überprüft.
Dies ist eine Optimierung durch den Compiler.
Bei der Auswertung: %code% anstelle von:
erstellt zuerst ein temporäres Objekt über %code% ;
Konstruieren von %code% durch den Kopierkonstruktor und Übergeben des temporären;
Der Compiler erstellt einfach %code% mit %code% .
Ich habe den Unterschied zwischen Direktinitialisierung und Kopierinitialisierung (§8.5 / 12) gelesen:
%Vor%Was ich verstehe, wenn ich über Kopier-Initialisierung ist, dass benötigt wird zugänglich & amp; nicht expliziter Kopierkonstruktor , sonst würde das Programm nicht kompilieren. Ich habe es verifiziert, indem ich den folgenden Code geschrieben habe:
%Vor%GCC gibt einen Fehler ( ideone ) aus und sagt:
prog.cpp: 8: Fehler: 'A :: A (const A & amp;)' ist privat
Bis jetzt ist alles in Ordnung, bekräftigt, was Herb Sutter sagt ,
Die Initialisierung der Kopie bedeutet, dass das Objekt mit dem Kopierkonstruktor nach dem ersten Aufruf einer benutzerdefinierten Konvertierung initialisiert wird und der Form "T t = u;" entspricht:
Danach habe ich den copy-ctor zugänglich gemacht, indem ich das %code% Schlüsselwort kommentiert habe. Jetzt würde ich natürlich Folgendes erwarten:
A (const A & amp;)
Aber zu meiner Überraschung druckt das stattdessen ( ideone ):
A (int i)
Warum?
Gut, ich verstehe, dass zuerst ein temporäres Objekt vom Typ %code% aus %code% erstellt wird, das %code% type ist, indem %code% verwendet wird, indem die Konvertierungsregel hier angewendet wird (§8.5 / 14 ), und dann sollte copy-ctor aufgerufen werden, um %code% zu initialisieren. Aber es tat es nicht. Warum?
Wenn eine Implementierung es erlaubt, Copy-Konstruktor (§8.5 / 14) aufzurufen, warum akzeptiert sie dann nicht den Code, wenn der Copy-Konstruktor %code% deklariert wird? Schließlich nennt es es nicht. Es ist wie ein verwöhntes Kind, das zuerst irritierend um ein spezifisches Spielzeug bittet, und wenn du ihm eines gibst, das spezifische , wirft er es hinter deinem Rücken weg. : |
Könnte dieses Verhalten gefährlich sein? Ich meine, ich könnte ein anderes nützliches Ding im copy-ctor machen, aber wenn es es nicht aufruft, ändert es dann nicht das Verhalten des Programms?