Warum ist ein öffentlicher Kopierkonstruktor erforderlich, auch wenn er nicht aufgerufen wird?

8

Wenn Sie einen öffentlichen Kopierkonstruktor haben, wird das kleine Programm erstellt kompilieren, aber nicht den Nebeneffekt "Kopieren" anzeigen.

%Vor%     
Dieter Lücking 08.12.2013, 15:53
quelle

4 Antworten

4

Hier sind die relevanten Bits des C ++ Standards, die beteiligt sind:

  

[dcl.init] / 16, bullet 6, sub-bullet 1: Wenn die Initialisierung eine direkte Initialisierung ist oder wenn es sich um eine Kopierinitialisierung handelt, bei der die cv-unqualifizierte Version des Quelltyps dieselbe Klasse wie oder eine abgeleitete Klasse von, die Klasse des Ziels, Konstruktoren werden berücksichtigt .... Wenn kein Konstruktor angewendet wird oder die Überladungsauflösung mehrdeutig ist, ist die Initialisierung nicht korrekt. [Hervorhebung hinzugefügt]

Mit anderen Worten, es spielt keine Rolle, ob eine Compileroptimierung für die Kopie geeignet ist, die Initialisierung ist schlecht, weil es keine anwendbaren Konstruktoren gibt. Nachdem Sie den Copy-Constructor veröffentlicht haben, gilt natürlich folgender Abschnitt:

  

[class.copy] / 31: Wenn bestimmte Kriterien erfüllt sind, darf eine Implementierung den Kopier- / Verschiebungsaufbau eines Klassenobjekts auslassen, selbst wenn der Kopier- / Verschiebungskonstruktor und / oder der Destruktor für das Objekt Nebeneffekte haben .... Diese Eliminierung von Kopier- / Verschiebevorgängen, die als Kopie Elision bezeichnet wird, ist unter den folgenden Umständen zulässig (die kombiniert werden können, um mehrere Kopien zu eliminieren):

     

bullet 3: Wenn ein temporäres Klassenobjekt, das nicht an eine Referenz (12.2) gebunden wurde, in ein Klassenobjekt mit dem gleichen cv-unqualifizierten Typ kopiert / verschoben wird, kann die Kopier- / Verschiebungsoperation weggelassen werden, indem die temporäres Objekt direkt in das Ziel der ausgelassenen Kopie / Verschiebung

    
godel9 08.12.2013, 16:20
quelle
8

Sie haben die sogenannte "Kopierinitialisierung" verwendet (definiert in [decl.init] ). Die definierte Bedeutung besteht darin, ein temporäres Objekt vom Typ X mit dem Konstruktor int zu konstruieren und dann x mit dem Kopierkonstruktor aus dem temporären zu initialisieren.

Der Standard erlaubt jedoch auch eine Optimierung namens "copy constructor elision" (definiert in [class.copy] ) in diesem Fall. Wenn diese Optimierung angewendet wird, gibt es keine temporäre. x wird mit dem int -Konstruktor erstellt, als hätten Sie so genannte "direkte Initialisierung" X x(1); .

geschrieben

Damit Sie nicht versehentlich Code schreiben, der kompiliert wird, wenn die Optimierung angewendet wird, aber nicht, wenn dies nicht der Fall ist, erfordert der Standard, dass der Kopierkonstruktor zugänglich sein muss, auch wenn er entfernt ist. Daher muss der Konstruktor öffentlich sein, obwohl er (mit dem Compiler und den Optionen, die Sie verwenden) nicht aufgerufen wird.

In C ++ werden 11 Move-Konstruktoren berücksichtigt und sind ebenfalls für Elision geeignet. Allerdings hat diese Klasse X keinen Verschiebungskonstruktor, daher sind C ++ 11 und C ++ 03 für dieses Beispiel identisch.

    
Steve Jessop 08.12.2013 16:10
quelle
2

Die Initialisierung wird vom Compiler optimiert auf:

%Vor%

Dies ist eine Art von Kopie Elision, und ist durch den Standard erlaubt, obwohl es Nebenwirkungen entfernen kann, wie Sie gesehen haben.

Aus dem C ++ 03-Standardabschnitt 12.8:

  

Wenn bestimmte Kriterien erfüllt sind, darf eine Implementierung ausgelassen werden   die Kopie Konstruktion eines Klassenobjekts, auch wenn der Kopierkonstruktor   und / oder Destruktor für das Objekt haben Nebenwirkungen. In solchen Fällen   Implementierung behandelt die Quelle und das Ziel der ausgelassenen Kopie   Operation als einfach zwei verschiedene Möglichkeiten, auf dasselbe zu verweisen   Objekt, und die Zerstörung dieses Objekts tritt bei der späteren der   Zeiten, in denen die beiden Objekte ohne die Zerstörung zerstört worden wären   Optimierung. 111) Diese Eliminierung von Kopiervorgängen ist erlaubt in der   folgende Umstände (die kombiniert werden können, um mehrere zu eliminieren   Kopien):      - in einer Anweisung in einer Funktion mit einem Klassenrückgabetyp,   wenn der Ausdruck der Name eines nichtflüchtigen automatischen Objekts ist   mit demselben cv-unqualifizierten Typ wie der Funktion return type, der   Die Kopieroperation kann weggelassen werden, indem das automatische Objekt konstruiert wird   direkt in den Rückgabewert der Funktion      - Wenn eine temporäre Klasse & gt; Objekt, das nicht an eine Referenz gebunden wurde (12.2)   würde in ein Klassenobjekt mit demselben cv-unqualifizierten Typ, der Kopie, kopiert werden   Die Operation kann weggelassen werden, indem das temporäre Objekt konstruiert wird   direkt in das Ziel der ausgelassenen Kopie

Der zweite Fall ist, was wir hier haben.

    
polkadotcadaver 08.12.2013 16:06
quelle
2

Meine beste Vermutung ist, dass dies eine Compiler-Optimierung ist. Es gibt einen gültigen Pfad zum Deklarieren eines Objekts auf diese Weise, wenn Sie einen Kopierkonstruktor haben. Erwägen Sie dies explizit:

%Vor%

Oder expliziter:

%Vor%

Also, anstatt operator= zu verwenden, weiß es, dass Sie versuchen, Ihr Objekt seit der Deklaration zu initialisieren. Im Wesentlichen ist der Compiler "schlau". Schließlich optimiert es dies wieder für diesen Schritt:

%Vor%

Nachdem der Compiler herausgefunden hat, was Sie zu tun versucht haben, wird dies zu einer Standardinitialisierung des Objekts.

BEARBEITEN

Beachten Sie die Vollständigkeit, wenn Sie Folgendes versuchen:

%Vor%

Sie werden das Problem mit privatem operator= sehen. Explizit ist es wichtig zu beachten, dass operator= niemals tatsächlich in der ursprünglichen Initialisierungsfrage oben verwendet wird, obwohl = im Code angezeigt wird.

    
RageD 08.12.2013 16:00
quelle

Tags und Links