Effizienz von C ++ 11 push_back () mit std :: move versus emplace_back () für bereits erstellte Objekte

83

In C ++ 11 emplace_back()ist im allgemeinen bevorzugt (in Bezug auf der Effizienz) zu , push_back()da es ermöglicht , in-Place - Konstruktion, aber das ist immer noch der Fall , bei der Verwendung push_back(std::move())mit einem bereits aufgebauten Objekt?

Wird zum Beispiel emplace_back()in Fällen wie den folgenden immer noch bevorzugt?

std::string mystring("hello world");
std::vector<std::string> myvector;

myvector.emplace_back(mystring);
myvector.push_back(std::move(mystring));
// (of course assuming we don't care about using the value of mystring after)

Gibt es im obigen Beispiel außerdem einen Vorteil, wenn Sie stattdessen Folgendes tun:

myvector.emplace_back(std::move(mystring));

oder ist der Umzug hierher völlig überflüssig oder hat er keine Auswirkungen?

Randalieren
quelle
myvector.emplace_back(mystring);kopiert und bewegt sich nicht. Die anderen beiden Züge sollten gleich sein.
TC
Siehe auch diese Frage und Ergebnisse: Angeforderte Umfrage für VC ++ zum Einfügen und Einfügen
ildjarn

Antworten:

114

Mal sehen, was die verschiedenen von Ihnen bereitgestellten Anrufe bewirken:

  1. emplace_back(mystring): Dies ist eine direkte Konstruktion des neuen Elements mit dem von Ihnen angegebenen Argument. Da Sie einen Wert angegeben haben, handelt es sich bei dieser In-Place-Konstruktion tatsächlich um eine Kopierkonstruktion, dh dies entspricht dem Aufrufenpush_back(mystring)

  2. push_back(std::move(mystring)): Dies ruft die Verschiebungseinfügung auf, die im Fall von std :: string eine direkte Verschiebungskonstruktion ist.

  3. emplace_back(std::move(mystring)): Dies ist wieder eine In-Place-Konstruktion mit den von Ihnen angegebenen Argumenten. Da dieses Argument ein r-Wert ist, nennt es den Verschiebungskonstruktor von std::string, dh es ist eine direkte Verschiebungskonstruktion wie in 2.

Mit anderen Worten, wenn es mit einem Argument vom Typ T aufgerufen wird, sei es ein r-Wert oder ein l-Wert emplace_backund push_backsind äquivalent.

Für alle anderen Argumente emplace_backgewinnt das Rennen jedoch, zum Beispiel mit einem char const*in a vector<string>:

  1. emplace_back("foo")fordert string::string(char const*)eine In-Place-Konstruktion.

  2. push_back("foo")Zuerst muss string::string(char const*)die implizite Konvertierung aufgerufen werden, die erforderlich ist, um mit der Signatur der Funktion übereinzustimmen, und dann eine Einfügung wie in Fall 2 oben. Daher ist es gleichbedeutend mitpush_back(string("foo"))

Arne Mertz
quelle
1
Der Verschiebungskonstruktor ist im Allgemeinen effizienter als die Kopierkonstruktoren, daher ist die Verwendung von rvalue (Fall 2, 3) trotz derselben Semantik effizienter als die Verwendung eines lvalue (Fall 1).
Ryan Li
Was ist mit einer solchen Situation? void foo (string && s) {vector.emplace (s); // 1 vector.emplace (std :: move (s)); // 2}
VALOD9
@VALOD das sind wieder die Nummern 1 und 3 meiner Liste. skann nur rvalues daß bindet , wie rvalue Referenz definiert werden, aber innerhalb von foo, sist ein L - Wert.
Arne Mertz
1

Der emplace_back ruft eine Liste von rvalue-Referenzen ab und versucht, ein Containerelement direkt an Ort und Stelle zu erstellen. Sie können emplace_back mit allen Typen aufrufen, die von den Containerelementkonstruktoren unterstützt werden. Wenn Sie emplace_back für Parameter aufrufen, die keine rvalue-Referenzen sind, wird auf normale Referenzen zurückgegriffen, und zumindest der Kopierkonstruktor wird aufgerufen, wenn der Parameter und die Containerelemente vom gleichen Typ sind. In Ihrem Fall sollte 'myvector.emplace_back (mystring)' eine Kopie der Zeichenfolge erstellen, da der Compiler nicht wissen konnte, dass der Parameter myvector beweglich ist. Fügen Sie also std :: move ein, um den gewünschten Nutzen zu erzielen. Der push_back sollte für bereits erstellte Elemente genauso funktionieren wie emplace_back.

Tunichtgut
quelle
2
Das ist eine ... interessante Art, Weiterleitungen / universelle Referenzen zu beschreiben.
TC