2013-01-12 21 views
9

Sto cercando di sapere di più sui riferimenti rvalue ma mi sono bloccato su questo semplice esempio:rvalue ciao mondo manca costruttore

#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; 
} 

ho compilato con Clang 3.2 e -std=c++11 -fno-elide-constructors (per evitare (N) RVO), ma il risultato è sorprendente per me:

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

mi aspettavo esattamente questo ad eccezione del primo ~C(). Da dove viene e cosa mi manca perché ci sono 2 costruzioni e 3 distruzioni? È il costruttore & & chiamato con un riferimento oggetto distrutto ??

+0

passo attraverso di essa in un debugger e vedere dove le chiamate sono provenienti da. Hai ragione che il numero di chiamate del costruttore e il numero di chiamate del distruttore dovrebbero essere le stesse. –

+0

Ho ricevuto questo http://liveworkspace.org/code/IlzNk$0 che è quanto previsto –

+2

@SethCarnegie: ma hai selezionato GCC 4.7.2 no? Ho provato a selezionare clang 3.2 e ottengo l'output mostrato dall'OP –

risposta

3

Questo deve essere un bug. Il distruttore per l'oggetto locale costruito in foo() viene richiamato prima del costruttore di spostamento dell'oggetto destinatario. In particolare, sembra che un temporaneo sia assegnato ma non (spostato) costruito quando si restituisce il valore. Il seguente programma illustra questo:

#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; 
} 

uscita:

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 

Creazione due oggetti all'interno di foo() sembra gettare più luce sulla questione:

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

uscita:

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 

È interessante notare che questo sembra dipendere dal modo in cui l'oggetto viene costruito in foo(). Se foo() è scritto in questo modo:

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

Poi appare l'errore. Se è scritto in questo modo non è così:

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

Uscita con quest'ultima definizione di 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

Ciò che è curioso è che funzioni come previsto: C foo() {return C (10) ; }. Sembra un bug del percorso di codice molto specifico. Accetterò questa risposta allora. – chrisaverage

+0

@chrisaverage: penso che il problema sia abbastanza generale. Se 'foo()' è scritto in questo modo: 'C foo() {C c (10); ritorno c; } 'allora dà l'errore. Solo se è scritto in questo modo non lo fa: 'C foo() {return C (10); } ' –

+0

Quello che intendevo è che si verifica solo quando l'oggetto è candidato per NRVO ma non RVO. Il bug report citato da Michael nei commenti originali sembra confermare questo. – chrisaverage