Seltsames Verhalten der Kopierinitialisierung, ruft den Copy-Konstruktor nicht auf!

7

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 private 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 A aus 10 erstellt wird, das int type ist, indem A(int i) verwendet wird, indem die Konvertierungsregel hier angewendet wird (§8.5 / 14 ), und dann sollte copy-ctor aufgerufen werden, um a 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 private 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?

    
Nawaz 28.05.2011, 16:56
quelle

6 Antworten

10

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%     
Steve Jessop 28.05.2011, 17:51
quelle
5

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.

    
Edward Loper 28.05.2011 18:02
quelle
4

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.

    
Prasoon Saurav 28.05.2011 17:20
quelle
1

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.

    
davka 28.05.2011 17:41
quelle
0

RVO und NRVO, Kumpel. Tadellos guter Fall der Kopie ellision.

    
Puppy 28.05.2011 17:04
quelle
0

Dies ist eine Optimierung durch den Compiler.

Bei der Auswertung: A a = 10; anstelle von:

  1. erstellt zuerst ein temporäres Objekt über A(int) ;

  2. Konstruieren von a durch den Kopierkonstruktor und Übergeben des temporären;

Der Compiler erstellt einfach a mit A(int) .

    
sergio 28.05.2011 17:09
quelle