Unerwartetes konstantes Referenzverhalten

8
%Vor%

In GCC 4.2 bekomme ich diese Nachricht:

%Vor%

Wenn ich das "private" von B entferne, bekomme ich die erwartete Ausgabe:

%Vor%

Meine Frage ist: Warum macht eine Methode, die nicht private genannt wird, eine Änderung, ob dieser Code kompiliert? Ist das Standard-Mandat? Gibt es eine Problemumgehung?

    
Zachary Vance 14.07.2010, 18:17
quelle

3 Antworten

4

Der wichtige Ausdruck im aktuellen Standard (C ++ 03) scheint in § 8.5.3 zu sein, der erklärt, wie Referenzen initialisiert werden (In diesen Anführungszeichen ist T1 der Typ der zu initialisierenden Referenz und T2 ist der Typ des Initialisierungsausdrucks).

  

Wenn der Initialization-Ausdruck ein rvalue ist, mit T2 ein class-Typ und " cv1 T1 " mit " cv2 T2 " referenzkompatibel ist, wird der Verweis auf eine der folgenden Arten gebunden (die Auswahl ist implementierungsdefiniert):

     

- Die Referenz ist an das durch den rvalue (siehe 3.10) dargestellte Objekt oder an ein Unterobjekt innerhalb dieses Objekts gebunden.

     

- Ein temporäres Objekt vom Typ " cv1 T2 " [sic] wird erstellt, und ein Konstruktor wird aufgerufen, um das gesamte rvalue-Objekt in das temporäre Objekt zu kopieren. Die Referenz ist an das temporäre Objekt oder an ein Unterobjekt innerhalb des temporären Objekts gebunden.

     

Der Konstruktor, der zum Erstellen der Kopie verwendet wird, muss aufrufbar sein, unabhängig davon, ob die Kopie tatsächlich erstellt wurde oder nicht.

Selbst wenn die Implementierung den Verweis direkt an das temporäre Objekt bindet, muss der Kopierkonstruktor zugänglich sein.

Beachten Sie, dass dies in C ++ 0x geändert wird, nach der Auflösung von CWG defekt 391 . Die neue Sprache lautet (N3092 §8.5.3):

  

Andernfalls, wenn T2 ein Klassentyp und

ist      

- Der Initialisierungsausdruck ist ein rvalue und " cv1 T1 " ist referenzkompatibel mit " cv2 T2 ",

     

- T1 ist nicht referenzbezogen auf T2 und der Initialisiererausdruck kann implizit in einen rvalue vom Typ " cv3 T3" " konvertiert werden (diese Konvertierung wird durch Aufzählung der anwendbaren Konvertierungsfunktionen ausgewählt (13.3.1.6) und Auswahl der besten durch Überladungsauflösung (13.3)),

     

dann ist die Referenz im ersten Fall an den Initialisiererausdruck rvalue und im zweiten Fall an das Objekt gebunden, das das Ergebnis der Konvertierung ist (oder in jedem Fall an das entsprechende Unterobjekt der Basisklasse des Objekts).

Der erste Fall wird angewendet und die Referenz wird direkt an den Initialisierungsausdruck gebunden.

    
James McNellis 14.07.2010, 19:03
quelle
3

Sie verwenden also 'copy-initialization':

  

8.5 / 11 Initialisierer

     

Die Form der Initialisierung (unter Verwendung von   Klammern oder =) ist in der Regel   unbedeutend, aber egal wann   Die initialisierte Entität hat a   Klassentyp; siehe unten. ...

     

Die Initialisierung in   Argumentübergabe, Funktionsrückgabe,   eine Ausnahme werfen (15.1), Handhabung   eine Ausnahme (15.3), und   In Klammern gesetzte Initialisierungslisten   (8.5.1) wird Kopierinitialisierung genannt   und entspricht dem Formular

%Vor%      

Die Initialisierung, die in new auftritt   Ausdrücke (5.3.4), static_cast   Ausdrücke (5.2.9), funktional   Notationstypkonvertierungen (5.2.3) und   Basis- und Elementinitialisierer (12.6.2)   heißt Direkt-Initialisierung und ist   entspricht dem Formular

%Vor%

In 13.3.1.3 "Initialisierung durch Konstruktor" lauten die Überladungen für den ausgewählten Konstruktor:

  

Wenn Objekte des Klassentyps direkt initialisiert (8.5) oder aus einem Ausdruck des gleichen oder eines abgeleiteten Klassentyps (8.5) initialisiert werden, wählt die Überladungsauflösung den Konstruktor aus. Für die direkte Initialisierung sind die Kandidatenfunktionen alle Konstruktoren der Klasse des Objekts, das initialisiert wird. Für die Kopierinitialisierung sind die Kandidatenfunktionen alle Konvertierungskonstruktoren (12.3.1) dieser Klasse.

Daher muss der Kopierkonstruktor für die Kopierinitialisierung verfügbar sein. Der Compiler darf jedoch die Kopie "optimieren":

12.2 / 1 Temporäre Objekte

  

Auch wenn die Erstellung des temporären Objekts vermieden wird (12.8), müssen alle semantischen Einschränkungen beachtet werden, so als ob das temporäre Objekt erstellt wurde. [Beispiel: Selbst wenn der Kopierkonstruktor nicht aufgerufen wird, müssen alle semantischen Einschränkungen, wie etwa die Zugänglichkeit (Abschnitt 11), erfüllt sein. ]

Sie können den gewünschten Effekt erhalten, indem Sie die Kopierinitialisierung vermeiden und die direkte Initialisierung verwenden:

%Vor%

Hinweis:

Da neuere Versionen von GCC anscheinend ein anderes Verhalten haben, dachte ich, ich würde diese Notiz veröffentlichen, die den Unterschied beheben könnte (wobei beide Verhaltensweisen immer noch den Standards entsprechen):

8.5.3 / 5 Referenzen sagt:

  

Wenn der Initialisierungsausdruck ein R-Wert ist, mit T2 ein Klassentyp und "cv1 T1" mit "cv2 T2" referenzkompatibel ist, wird die Referenz auf eine der folgenden Arten gebunden (die Auswahl ist implementierungsdefiniert) :

     
  • Die Referenz ist an das Objekt gebunden, das durch den rvalue (siehe 3.10) oder ein Unterobjekt darin repräsentiert wird   dieses Objekt.

  •   
  • Ein temporärer Typ vom Typ "cv1 T2" [sic] wird erstellt, und ein Konstruktor wird aufgerufen, um das gesamte rvalue-Objekt in das temporäre Objekt zu kopieren. Die Referenz ist an das temporäre Objekt oder an ein Unterobjekt innerhalb des temporären Objekts gebunden.

  •   

Der Konstruktor, der zum Erstellen der Kopie verwendet wird, muss aufrufbar sein, unabhängig davon, ob die Kopie tatsächlich erstellt wurde oder nicht.

Ich habe ursprünglich den letzten Satz gelesen ("der Konstruktor, der verwendet wird ..."), der auf beide Optionen angewendet wird, aber vielleicht sollte er nur als Option für die Option seconds gelesen werden - oder zumindest so ist der GCC Betreuer lesen es.

Ich bin mir nicht sicher, ob dies zwischen dem unterschiedlichen Verhalten der GCC-Versionen (Kommentare sind willkommen) geschieht. Wir erreichen definitiv die Grenzen meiner Sprachlehrerfähigkeiten ...

    
Michael Burr 14.07.2010 18:48
quelle
1

Ich denke, es ist in der Tat ein Compiler-Fehler, gcc scheint zu denken, dass das eine Kopierinitialisierung ist. Verwenden Sie stattdessen direkte Initialisierung:

%Vor%

Der Aufruf des Kopierkonstruktors in der Kopierinitialisierung wird immer weg optimiert (eine Kopie der Kopie) und muss dann nicht verfügbar sein.

    
Philipp 14.07.2010 18:47
quelle

Tags und Links