2012-08-14 14 views
6

Ich verwende den Code unten, um Testkopie elision:Warum elision nicht mit std :: move arbeiten?

class foo 
{ 
public: 
    foo() {cout<<"ctor"<<endl;}; 
    foo(const foo &rhs) {cout<<"copy ctor"<<endl;} 
}; 

int g(foo a) 
{ 
    return 0; 
} 

int main() 
{ 
    foo a; 
    g(std::move(a)); 
    return 0; 
} 

ich erwartet hatte nur der Standard-Konstruktor aufgerufen werden würde, weil das Argument der g() ein rvalue ist und Kopie wird elided werden. Das Ergebnis zeigt jedoch, dass sowohl der Standardkonstruktor als auch der Kopierkonstruktor aufgerufen werden. Warum?

Und wenn ich den Funktionsaufruf zu g(foo()) ändere, wird die Kopie gelöscht. Was ist der Unterschied zwischen den Rückgabetypen foo() und std::move(a)? Wie kann ich den Compiler auf einen Lvalue kopieren?

+4

Sie können nicht.'g' nimmt seinen Parameter als Wert, so dass der Compiler sicherstellen muss, dass das übergebene Objekt von jedem Objekt verschieden ist, auf das vom aufrufenden Bereich zugegriffen werden kann. Wenn das übergebene Objekt ein lvalue ist, gibt es keine temporäre zu eliminieren und eine Kopie kann nicht entfernt werden. –

+1

Wie viele Destruktor-Aufrufe haben Sie erwartet? ;) – curiousguy

+0

Vielleicht möchtest du [lesen] (http://stackoverflow.com/a/11540204/252000) was "std :: move" tatsächlich macht. – fredoverflow

Antwort

6

Kopie Elision für kann nur in einigen spezifischen Situationen auftreten, von denen die gebräuchlichste das Kopieren eines temporären ist (die anderen kehren Einheimische zurück und werfen/fangen Ausnahmen). Es wird kein temporärer Code erzeugt, daher wird keine Kopie gelöscht.

Der Copy-Konstruktor, weil foo keinen Umzug Konstruktor (move Bauer wird nicht implizit erzeugt für Klassen mit expliziten Kopierkonstruktoren) genannt zu werden, und so std::move(a) entspricht den foo(const foo &rhs) Konstruktor (die die Funktion Argument zu konstruieren, verwendet wird) .

Eine Kopie eines L-Wert kann in den folgenden Situationen elided werden (obwohl es keine Möglichkeit, Kraft ein Compiler ist die Auslassung auszuführen):

foo fn() { 
    foo localAutomaticVariable; 
    return localAutomaticVariable; //Copy to construct return value may be elided 
} 

int main() { 
    try { 
     foo localVariable; 
     throw localVariable; //The copy to construct the exception may be elided 
    } 
    catch(...) {} 
} 

Wenn Sie Kopien zu vermeiden, wenn Funktion übergeben Argumente, können Sie einen Zug Konstruktor verwenden, die die Ressourcen der Objekte stibitzt ihm gegeben:

class bar { 
public: 
    bar() {cout<<"ctor"<<endl;}; 
    bar(const bar &rhs) {cout<<"copy ctor"<<endl;} 
    bar(bar &&rhs) {cout<<"move ctor"<<endl;} 
}; 

void fn(bar a) 
{ 
} 
//Prints: 
//"ctor" 
//"move ctor" 
int main() 
{ 
    bar b; 
    f(std::move(b)); 
} 

auch, wenn Kopie elision erlaubt, aber nicht mehr auftritt, wird der Umzug Konstruktor verwendet werden, wenn es availab ist le.

+0

+1, es kann erwähnenswert sein, dass es standardmäßig keinen Move-Konstruktor gibt, also musste die Konstruktion des Arguments auch als r-Wert auf den Copy-Konstruktor zurückfallen. – KillianDS

4

Sie müssen erklären g als:

int g(foo && a) //accept argument as rvalue reference 
{ 
    return 0; 
} 

Jetzt kann es Argument von R-Wert-Referenz akzeptieren.

In Ihrem Fall, obwohl der Ausdruck std::move(a) rvalue erzeugt, bindet es nicht an einen Parameter, der das Argument durch den Wert akzeptiert. Das Empfangsende muss ebenfalls rvalue-reference sein.

Bei g(foo()) wird die Kopie vom Compiler ausgeführt, was eine Optimierung darstellt. Es ist keine Anforderung von der Sprache[until C++17]. Sie können diese Optimierung deaktivieren, wenn Sie möchten: dann verhalten sich g(foo()) und g(std::move(a)) genau wie erwartet.

Aber wenn Sie g ändern, wie ich oben vorgeschlagen, der Anruf g(foo()) wird keine Kopie machen, weil es eine Anforderung durch die Sprache ist nicht Kopie mit & & zu machen. Es ist keine Compiler-Optimierung mehr.

+5

Ich denke, du verpasst den Punkt der Frage. Es wird keine Kopie mit dieser Signatur geben, nur weil es kein Kopieren gibt. – hvd

+0

@hvd: Ich habe Ihren Kommentar nicht verstanden. Was ist die Grundlage dafür: * "Es wird keine Kopie Elision mit dieser Unterschrift geben, nur weil es kein Kopieren gibt" *? – Nawaz

+2

Man könnte argumentieren, dass copy-ellision per definitionem eine Optimierung durch den Compiler ist (obwohl ich mir nicht sicher bin, ob das korrekt ist). In diesem Fall ist Ihre Lösung keine Kopie-Ellision. –

Verwandte Themen