2013-01-12 18 views
9

Ich versuche, mehr über rvalue Referenzen zu wissen, aber habe ich mich auf diesem einfachsten Beispiel stecken:rvalue Hallo Welt fehlt Konstruktor

#include <iostream> 
using namespace std; 

struct C { 
    C() { cout << "C()\n"; } 
    ~C() { cout << "~C()\n"; } 
    C(const C&) { cout << "C(const C&)\n"; } 
    C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; } 

    C(C&&) { cout << "C(C&&)\n"; } 
    C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; } 
}; 

C foo() { C c; return c; } 

int main() 
{ 
    const C c = foo(); 
    return 0; 
} 

Ich habe es mit Clang kompiliert 3.2 und -std=c++11 -fno-elide-constructors (zur Vermeidung von (N) RVO), aber das Ergebnis ist für mich überraschend:

C() 
~C() // huh? 
C(C&&) 
~C() 
~C() 

ich erwartet hatte genau, dass mit Ausnahme der ersten ~C(). Woher kam es und was fehlt mir, weil es 2 Konstruktionen und 3 Zerstörungen gibt? Wird der & & Konstruktor mit einer zerstörten Objektreferenz aufgerufen ??

+0

Schritt durch sie in einem Debugger und sehen, wo die Anrufe herkommen. Sie haben Recht, dass die Anzahl der Konstruktoraufrufe und die Anzahl der Destruktoraufrufe identisch sein sollten. –

+0

Ich habe diese http://liveworkspace.org/code/IlzNk$0 bekommen was erwartet wird –

+2

@SethCarnegie: aber Sie haben GCC 4.7.2 gewählt, nicht wahr? Ich habe versucht, clang 3.2 auszuwählen, und ich bekomme die Ausgabe, die durch das OP –

Antwort

3

Dies muss ein Fehler sein. Der Destruktor für das lokale Objekt, das in foo() erstellt wird, wird vor dem Verschiebungskonstruktor des empfangenden Objekts aufgerufen. Insbesondere scheint ein temporärer zugewiesen, aber nicht (move-) konstruiert bei Rückgabe nach Wert. Das folgende Programm zeigt dies:

#include <iostream> 
using namespace std; 

struct C 
{ 
    C(int z) { id = z; cout << "C():" << id << endl; } 
    ~C() { cout << "~C():" << id << endl; } 
    C(const C& c) { id = c.id + 1; cout << "C(const C&):" << id << endl; } 
    C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; } 
    C(C&& c) { id = c.id + 1; cout << "C(C&&):" << id << endl;} 
    C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; } 
    int id; 
}; 

C foo() { C c(10); return c; } 

int main() 
{ 
    const C c = foo(); 
    return 0; 
} 

Ausgang:

C():10 
// THE TEMPORARY OBJECT IS PROBABLY ALLOCATED BUT *NOT CONSTRUCTED* HERE... 
~C():10 // DESTRUCTOR CALLED BEFORE ANY OTHER OBJECT IS CONSTRUCTED! 
C(C&&):4198993 
~C():4198992 
~C():4198993 

Erstellen zwei Objekte innerhalb von foo() scheint etwas mehr Licht auf die Frage zu werfen:

C foo() { C c(10); C d(14); return c; } 

Ausgang:

C():10 
C():14 
~C():14 
// HERE, THE CONSTRUCTOR OF THE TEMPORARY SHOULD BE INVOKED! 
~C():10 
C(C&&):1 // THE OBJECT IN main() IS CONSTRUCTED FROM A NON-CONSTRUCTED TEMPORARY 
~C():0 // THE NON-CONSTRUCTED TEMPORARY IS BEING DESTROYED HERE 
~C():1 

Interessanterweise scheint dies davon abhängig zu sein, wie das Objekt in foo() konstruiert wird. Wenn foo() auf diese Weise geschrieben wird:

C foo() { C c(10); return c; } 

Dann wird der Fehler angezeigt. Wenn es auf diese Weise geschrieben wird, tut es nicht:

C foo() { return C(10); } 

Ausgabe mit dieser letzten Definition von foo():

C():10 // CONSTRUCTION OF LOCAL OBJECT 
C(C&&):11 // CONSTRUCTION OF TEMPORARY 
~C():10 // DESTRUCTION OF LOCAL OBJECT 
C(C&&):12 // CONSTRUCTION OF RECEIVING OBJECT 
~C():11 // DESTRUCTION OF TEMPORARY 
~C():12 // DESTRUCTION OF RECEIVING OBJECT 
+0

Was ist seltsam, dass dies wie erwartet funktioniert: C foo() {return C (10) ; }. Scheint wie ein sehr spezifischer Code Path Bug. Ich werde diese Antwort dann akzeptieren. – chrisaverage

+0

@chrisaverage: Ich denke, das Problem ist ziemlich allgemein. Wenn 'foo()' so geschrieben wird: 'C foo() {C c (10); Rückkehr c; } 'dann gibt es den Fehler. Nur wenn es so geschrieben ist, heißt das nicht: 'C foo() {return C (10); } ' –

+0

Was ich meinte ist, dass es nur auftritt, wenn das Objekt ein Kandidat für NRVO, aber nicht für RVO ist. Der von Michael in den Originalkommentaren erwähnte Fehlerbericht scheint dies zu bestätigen. – chrisaverage