2013-01-12 17 views
9

Próbuję dowiedzieć się więcej o referencje rvalue ale utknąłem na tym najprostszym przykładzie:RValue hello world brakuje 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; 
} 

Mam skompilowany z Clang 3.2 i -std=c++11 -fno-elide-constructors (w celu uniknięcia (N) RVO), ale wynik jest zaskakujący do mnie:

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

spodziewałem się dokładnie, że z wyjątkiem pierwszego ~C(). Skąd się wziął i czego mi brakuje, ponieważ są 2 konstrukcje i 3 zniszczenia? Czy wywoływany jest konstruktor & & ze zniszczoną referencją obiektu?

+0

Krok przez nią w debugera i zobaczyć, gdzie rozmowy pochodzą. Masz rację, że liczba wywołań konstruktora i liczba wywołań destruktora powinny być takie same. –

+0

Mam to http://liveworkspace.org/code/IlzNk$0 co jest oczekiwane –

+2

@SethCarnegie: ale wybrałeś GCC 4.7.2, prawda? Próbowałem wybrać klang 3.2 i otrzymałem wynik pokazany przez OP –

Odpowiedz

3

To musi być błąd. Destruktor lokalnego obiektu zbudowanego w foo() jest wywoływany przed konstruktorem ruchu obiektu odbierającego. W szczególności, wydaje się tymczasowy jest alokowany, ale nie (ruch-) skonstruowany po powrocie przez wartość. Poniższy program pokazuje to:

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

wyjściowa:

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 

Tworzenie dwa obiektów wewnątrz foo() wydaje się rzucić nieco więcej światła na ten problem:

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

wyjściowa:

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 

Co ciekawe, wydaje się to zależeć od tego, jak obiekt jest skonstruowany w foo(). Jeśli foo() jest napisane w ten sposób:

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

Następnie pojawia się błąd. Jeśli to jest napisane w ten sposób, że nie:

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

Wyjście z tej ostatniej definicji 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

Co ciekawe, działa to zgodnie z oczekiwaniami: C foo() {return C (10) ; }. Wygląda na bardzo specyficzny błąd ścieżki kodu. Przyjmuję tę odpowiedź wtedy. – chrisaverage

+0

@chraiverage: Myślę, że problem jest dość ogólny. Jeśli 'foo()' jest napisane w ten sposób: 'C foo() {C c (10); return c; } 'następnie daje błąd. Tylko jeśli jest napisane w ten sposób, to nie: 'C foo() {return C (10); } ' –

+0

Co mam na myśli to, że występuje tylko wtedy, gdy obiekt jest kandydatem na NRVO, ale nie RVO. Raport o błędzie wspomniany przez Michaela w oryginalnych komentarzach zdaje się to potwierdzać. – chrisaverage