Konstruktor / Zuweisungsoperator für private Kopien und Kopierinitialisierung

8

Dies ist eine Folge von diese Frage

im folgenden Code, warum Zeile 1 kompiliert, während Zeile 2 und 3 nicht (mit Visual C ++ 2010)

%Vor%

(Anmerkung: BA ist eine Kopie von boost :: nicht kopierbar)

bearbeiten: Mein Problem ist nicht zu wissen, warum Zeile 2 und 3 nicht kompilieren (ich weiß, dass der Kopierkonstruktor privat ist), aber warum Zeile 1 tut.

    
foke 29.06.2011, 16:40
quelle

6 Antworten

5

Der Compiler akzeptiert die erste Verwendung falsch. Selbst wenn die Kopie entfernt wird, muss der Kopierkonstruktor zugänglich sein, damit der Code korrekt ist.

In diesem speziellen Fall gibt es einen implizit deklarierten Kopierkonstruktor in A :

  

§12.8 / 4 Wenn die Klassendefinition einen Kopierkonstruktor nicht explizit deklariert, wird einer implizit deklariert.

Das ist implizit definiert:

  

§12.8 / 7 Ein implizit deklarierter Kopierkonstruktor ist implizit definiert, wenn er verwendet wird, um ein Objekt seines Klassentyps aus einer Kopie eines Objekts seines Klassentyps oder eines von seinem Klassentyp abgeleiteten Klassentyps zu initialisieren108). [Anmerkung: Der Kopierkonstruktor ist implizit definiert, selbst wenn die Implementierung seine Verwendung weggelassen hat (12.2). ] Ein Programm ist schlecht gemacht , wenn die Klasse, für die ein Kopierkonstruktor implizit definiert ist,

hat      

- ein nicht statisches Datenelement vom Klassentyp (oder Array davon) mit einem nicht zugänglichen oder mehrdeutigen Kopierkonstruktor oder

     

- Eine Basisklasse mit einem nicht zugänglichen oder mehrdeutigen Kopierkonstruktor.

    
David Rodríguez - dribeas 29.06.2011, 17:34
quelle
6

Tatsächlich sollte Zeile 1 nicht kompiliert werden.

Anscheinend hat vc ++ 2010 in diesem Fall Probleme beim Erzwingen von Sprachregeln (möglicherweise, weil sie mit einer Basisklasse und nicht mit dem Objekt selbst zusammenhängen).

g ++ Diagnosemeldung über Zeile 1 ist sehr klar

%Vor%     
6502 29.06.2011 17:03
quelle
2
%Vor%

Der Kopierkonstruktor wird als privat deklariert, so dass keine Kopie des Klassenobjekts mit diesem privaten Kopierkonstruktor erstellt werden kann, was zu einem Fehler führt.

%Vor%

Verwendet A::A(const A&) , um ein neues Objekt A zu erstellen. A wird von ABase abgeleitet und ruft auf ABase::ABase(const ABase&) in seinem Konstruktor, der für ihn nicht selbst kompiliert wird.

Hier ist die Ausgabe von Ideone. Es kompiliert nicht einmal auf gcc.

Warum funktioniert es in Visual Studio?
Der Grund ist eine mögliche Rückgabewertoptimierung durch den visuellen C ++ - Compiler, der den Kopierkonstruktor elimiert.

Gemäß dem C ++ - Standard 12.8 Kopieren von Klassenobjekten Abschnitt 15

Wenn bestimmte Kriterien erfüllt sind, darf eine Implementierung die Kopierkonstruktion eines Klassenobjekts weglassen, selbst wenn der Kopierkonstruktor und / oder der Destruktor für das Objekt Nebeneffekte haben. In solchen Fällen behandelt die Implementierung die Quelle und das Ziel der ausgelassenen Kopieroperation als einfach zwei verschiedene Arten, auf dasselbe Objekt Bezug zu nehmen, und die Zerstörung dieses Objekts erfolgt zu einem späteren Zeitpunkt, wenn die beiden Objekte gewesen wären zerstört ohne die Optimierung.111)

Siehe meine Antwort hier welches den Standard und den Beispielcode diesbezüglich zitiert.

%Vor%

Kompiliert nicht aus dem gleichen Grund, dass ABase::ABase(const ABase&) privat ist.

%Vor%

Kompiliert nicht, da B( const B& ); privat ist.

    
Alok Save 29.06.2011 16:47
quelle
1

Zeile 1 ist eine Rückgabewertoptimierung (der Compiler sieht, dass keine temporäre Variable für A() erstellt werden muss und der Kopierkonstruktor / Zuweisungsoperator der Variablen a zugewiesen wird). Dies kompiliert jedoch nicht auf GCC (Version 4.2.1) und sollte vermieden werden.

Zeile 2 wird nicht kompiliert, da der Compiler in diesem Fall keinen Zuweisungsoperator für Sie generiert. Zeile 3 kompiliert nicht wie erwartet.

Reagan Zusammenfassung: Zeile 1 funktioniert, weil es Microsoft ist, die anderen verhalten sich wie erwartet.

    
larsmoa 29.06.2011 16:58
quelle
1

Warum kompiliert Zeile 1? Weil dein Compiler kaputt ist; es sollte nicht, nach dem Standard. Während Ihre Klasse A implizit hat Erklärter Copy Constructor, §12.8 / 7 des Standards besagt, dass An Implizit deklarierter Copy-Konstruktor wird implizit definiert, wenn dies der Fall ist verwendet, um ein Objekt zu initialisieren (wie in A a = A(); ), und dass ein Programm ist schlecht gebildet (und somit eine Diagnose erforderlich), wenn der Konstruktor ist implizit defiend und eine Basisklasse hat einen unzugänglichen oder mehrdeutigen Konstruktor kopieren Es gibt sogar eine Notiz, dass dies der Fall ist, selbst wenn Die Implementierung unterscheidet den Kopierkonstruktor. Du bist Compiler nicht weit genug gehen: es sieht die implizit erklärte öffentliche Kopie Konstruktor, aber es versucht nicht, es implizit zu definieren, obwohl die Standard sagt eindeutig, dass es sollte.

    
James Kanze 29.06.2011 17:54
quelle
0

Ich glaube, das liegt daran, dass der Konstruktor geschützt ist, nicht privat. Der vom Compiler bereitgestellte Konstruktor in Klasse A kann den geschützten Konstruktor der Klasse ABase frei aufrufen, also funktioniert es.

Außerdem ist Zeile 1 kein Kopierkonstruktor. Eine Deklaration mit einer Zuweisung ist ein Sonderfall, der in einen Konstruktor umgewandelt wird.

    
Zan Lynx 29.06.2011 16:52
quelle

Tags und Links