push_back gegen emplace_back

Lesezeit: 9 Minuten

push back gegen emplace back
Ronag

Ich bin etwas verwirrt, was den Unterschied zwischen angeht push_back und emplace_back.

void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);

Da es eine push_back Überladung mit einer Rvalue-Referenz Ich verstehe nicht ganz, was der Zweck ist emplace_back wird?

  • Gute Lektüre hier: open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2642.pdf

    – Johann Kotlinski

    29. November 2010 um 14:00 Uhr

  • Beachten Sie, dass (wie Thomas unten sagt) der Code in der Frage von MSVS stammt Emulation von C++0x, nicht was C++0x eigentlich ist.

    – ich22

    21. Dezember 2010 um 5:50 Uhr

  • Ein besser zu lesender Artikel wäre: open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2345.pdf. N2642 ist hauptsächlich eine Formulierung für den Standard; N2345 ist das Papier, das die Idee erklärt und motiviert.

    – Alan

    13. März 2013 um 23:10 Uhr

  • Beachten Sie, dass es sogar in MSVC10 eine template <class _Valty> void emplace_back(_Valty&& _Val) Version, die eine dauert universelle Referenz die perfekte Weiterleitung an bietet explicit Konstruktoren mit einem Argument.

    – joki

    11. Juli 2017 um 19:10 Uhr


  • Verwandte: Gibt es einen Fall, wo push_back ist vorzuziehen emplace_back? Der einzige Fall, an den ich denken kann, ist, wenn eine Klasse irgendwie kopierbar wäre (T&operator=(constT&)) aber nicht konstruierbar (T(constT&)), aber ich kann mir nicht vorstellen, warum man das jemals wollen sollte.

    – Ben

    25. Mai 2018 um 18:44 Uhr

push back gegen emplace back
Thomas Petit

Zusätzlich zu dem, was der Besucher gesagt hat:

Die Funktion void emplace_back(Type&& _Val) bereitgestellt von MSCV10 ist nicht konform und redundant, da es, wie Sie angemerkt haben, absolut gleichwertig ist push_back(Type&& _Val).

Aber die echte C++0x-Form von emplace_back ist wirklich praktisch: void emplace_back(Args&&...);

Anstatt eine zu nehmen value_type es braucht eine variadische Liste von Argumenten, was bedeutet, dass Sie jetzt die Argumente perfekt weiterleiten und direkt ein Objekt in einen Container konstruieren können, ohne überhaupt ein Temporär.

Das ist nützlich, denn egal wie viel Cleverness RVO und Move-Semantik auf den Tisch bringen, es gibt immer noch komplizierte Fälle, in denen ein Push_back wahrscheinlich unnötige Kopien (oder Moves) anfertigt. Zum Beispiel mit dem Traditionellen insert() Funktion von a std::mapmüssen Sie eine temporäre erstellen, die dann in eine kopiert wird std::pair<Key, Value>die dann in die Karte kopiert werden:

std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";

// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString))); 

// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);

Warum haben sie also nicht die richtige Version von emplace_back in MSVC implementiert? Eigentlich hat es mich vor einer Weile auch genervt, also habe ich die gleiche Frage zum Thema gestellt Visual C++-Blog. Hier ist die Antwort von Stephan T. Lavavej, dem offiziellen Betreuer der Visual C++-Standardbibliotheksimplementierung bei Microsoft.

F: Sind Beta-2-emplace-Funktionen derzeit nur eine Art Platzhalter?

A: Wie Sie vielleicht wissen, sind variadische Vorlagen nicht in VC10 implementiert. Wir simulieren sie mit Präprozessormaschinen für Dinge wie
make_shared<T>()Tupel und die neuen Dinge in <functional>. Diese Vorverarbeitungsmaschinerie ist relativ schwierig zu verwenden und zu warten. Außerdem wirkt sich dies erheblich auf die Kompilierungsgeschwindigkeit aus, da wir wiederholt Unterüberschriften einfügen müssen. Aufgrund einer Kombination aus Zeitbeschränkungen und Bedenken hinsichtlich der Kompilierungsgeschwindigkeit haben wir keine variadischen Vorlagen in unseren Emplace-Funktionen simuliert.

Wenn verschiedene Vorlagen im Compiler implementiert sind, können Sie davon ausgehen, dass wir sie in den Bibliotheken nutzen, einschließlich in unseren emplace-Funktionen. Wir nehmen Konformität sehr ernst, können aber leider nicht alles auf einmal erledigen.

Es ist eine nachvollziehbare Entscheidung. Jeder, der nur einmal versucht hat, Variadic-Templates mit Präprozessor-Schreckenstricks zu emulieren, weiß, wie ekelhaft dieses Zeug wird.

  • Diese Klarstellung, dass es sich um ein MSVS10-Problem und nicht um ein C++-Problem handelt, ist hier der wichtigste Teil. Danke.

    – ich22

    21. Dezember 2010 um 5:49 Uhr

  • Ich glaube, Ihre letzte Zeile von C++-Code wird nicht funktionieren. pair<const int,Complicated> hat keinen Konstruktor, der ein int, ein weiteres int, ein double und als 4. Parameter einen String akzeptiert. Aber du kann Konstruieren Sie dieses Paarobjekt direkt mit seinem stückweisen Konstruktor. Die Syntax wird natürlich anders sein: m.emplace(std::piecewise,std::forward_as_tuple(4),std::forward_as_tuple(anInt,aDouble,aString));

    – Sellibitze

    14. März 2013 um 15:08 Uhr

  • Glücklicherweise werden unterschiedliche Vorlagen in VS2013 enthalten sein, jetzt in der Vorschau.

    – Daniel Earwicker

    25. Juli 2013 um 20:37 Uhr

  • Sollte diese Antwort aktualisiert werden, um die neuen Entwicklungen in vs2013 widerzuspiegeln?

    – wink

    27. Februar 2014 um 16:20 Uhr

  • Wenn Sie Visual Studio 2013 oder höher verwenden jetztSie sollten Unterstützung für das “Echte” haben emplace_back solange es in Visual C++ implementiert wurde, als variadische Vorlagen hinzugefügt wurden: msdn.microsoft.com/en-us/library/hh567368.aspx

    – kayleeFrye_onDeck

    28. Juni 2017 um 20:17 Uhr

1647154816 771 push back gegen emplace back
Besucher

emplace_back sollte kein Argument vom Typ annehmen vector::value_typesondern stattdessen variadische Argumente, die an den Konstruktor des angehängten Elements weitergeleitet werden.

template <class... Args> void emplace_back(Args&&... args); 

Es ist möglich, a zu bestehen value_type die an den Kopierkonstruktor weitergeleitet werden.

Da die Argumente weitergeleitet werden, bedeutet dies, dass, wenn Sie keinen rvalue haben, dies immer noch bedeutet, dass der Container eine “kopierte” Kopie speichert, keine verschobene Kopie.

 std::vector<std::string> vec;
 vec.emplace_back(std::string("Hello")); // moves
 std::string s;
 vec.emplace_back(s); //copies

Aber das obige sollte identisch mit dem sein, was push_back tut. Es ist wahrscheinlich eher für Anwendungsfälle gedacht wie:

 std::vector<std::pair<std::string, std::string> > vec;
 vec.emplace_back(std::string("Hello"), std::string("world")); 
 // should end up invoking this constructor:
 //template<class U, class V> pair(U&& x, V&& y);
 //without making any copies of the strings

  • @David: aber dann bist du umgezogen s im Umfang, ist das nicht gefährlich?

    – Matthias M.

    29. November 2010 um 17:56 Uhr

  • Es ist nicht gefährlich, wenn Sie s wegen seines Wertes nicht mehr verwenden möchten. Das Verschieben macht s nicht ungültig, das Verschieben stiehlt nur die bereits in s vorgenommene interne Speicherzuweisung und belässt es in einem Standardzustand (kein Stich zugewiesen), der nach der Zerstörung in Ordnung ist, als hätten Sie gerade std::string str;

    – David

    30. November 2010 um 0:52 Uhr

  • @David: Ich bin mir nicht sicher, ob ein verschobenes Objekt für jede Verwendung außer der anschließenden Zerstörung gültig sein muss.

    – Ben Voigt

    12. Dezember 2010 um 1:31 Uhr

  • vec.emplace_back("Hello") wird funktionieren, da die const char* Argument wird sein weitergeleitet zum string Konstrukteur. Das ist der springende Punkt emplace_back.

    – Alexander C.

    9. August 2011 um 20:28 Uhr

  • @BenVoigt: Ein verschobenes Objekt muss sich in einem gültigen (aber nicht spezifizierten) Zustand befinden. Dies bedeutet jedoch nicht unbedingt, dass Sie jede Operation daran ausführen können. Erwägen std::vector. Ein leeres std::vector ist ein gültiger Zustand, aber Sie können nicht anrufen front() darauf. Das bedeutet, dass jede Funktion, die keine Vorbedingungen hat, trotzdem aufgerufen werden kann (und Destruktoren niemals Vorbedingungen haben können).

    – David Stein

    10. Mai 2012 um 4:06 Uhr

1647154817 39 push back gegen emplace back
vadikrobot

Optimierung für emplace_back kann im nächsten Beispiel demonstriert werden.

Zum emplace_back Konstrukteur A (int x_arg) wird angerufen werden. Und für
push_back A (int x_arg) heißt zuerst und move A (A &&rhs) wird danach aufgerufen.

Der Konstruktor muss natürlich als gekennzeichnet sein explicitaber für das aktuelle Beispiel ist es gut, die Explizitheit zu entfernen.

#include <iostream>
#include <vector>
class A
{
public:
  A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }
  A () { x = 0; std::cout << "A ()\n"; }
  A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }
  A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }

private:
  int x;
};

int main ()
{
  {
    std::vector<A> a;
    std::cout << "call emplace_back:\n";
    a.emplace_back (0);
  }
  {
    std::vector<A> a;
    std::cout << "call push_back:\n";
    a.push_back (1);
  }
  return 0;
}

Ausgang:

call emplace_back:
A (x_arg)

call push_back:
A (x_arg)
A (A &&)

  • Ich kam hierher, nachdem ich bemerkt hatte, dass ich Code hatte, der anrief v.emplace_back(x); wobei x explizit bewegungskonstruierbar, aber nur explizit kopierkonstruierbar ist. Die Tatsache, dass emplace_back “implizit” explizit ist, lässt mich denken, dass meine Go-to-Funktion zum Anhängen wahrscheinlich sein sollte push_back. Gedanken?

    – Ben

    25. Oktober 2019 um 19:19 Uhr

  • Wenn Sie anrufen a.emplace_back Beim zweiten Mal wird der Move-Konstruktor aufgerufen!

    –AndreasK.

    21. Mai 2020 um 15:18 Uhr

  • @AndreasK. Das hängt nicht mit emplace_back zusammen, sondern erweitert die Größe des Vektors. Sie können dies überprüfen, indem Sie drucken, was verschoben wird, anstatt nur "A (A &&)\n"drucken "A (A &&) on " << rhs.x << "\n". Du kannst es sehen in diesem bearbeiteten Codeausschnitt.

    – Brambor

    25. August 2021 um 22:09 Uhr


Noch ein Beispiel für Listen:

// constructs the elements in place.                                                
emplace_back("element");

// creates a new object and then copies (or moves) that object.
push_back(ExplicitDataType{"element"});

1647154818 443 push back gegen emplace back
vaibhav kumar

Spezifischer Anwendungsfall für emplace_back: Wenn Sie ein temporäres Objekt erstellen müssen, das dann in einen Container verschoben wird, verwenden Sie emplace_back anstatt push_back. Das Objekt wird direkt im Container erstellt.

Anmerkungen:

  1. push_back im obigen Fall wird ein temporäres Objekt erstellt und in den Container verschoben. In-Place-Konstruktion verwendet jedoch für emplace_back wäre performanter, als das Objekt zu konstruieren und dann zu verschieben (was im Allgemeinen etwas Kopieren erfordert).
  2. Im Allgemeinen können Sie verwenden emplace_back anstatt push_back in allen Fällen ohne große Probleme. (Siehe Ausnahmen)

1647154818 151 push back gegen emplace back
Dharma

Ein schöner Code für push_back und emplace_back wird hier gezeigt.

http://en.cppreference.com/w/cpp/container/vector/emplace_back

Sie können die Verschiebungsoperation auf push_back und nicht auf emplace_back sehen.

emplace_back konforme Implementierung wird Argumente an die weiterleiten vector<Object>::value_typeKonstruktor, wenn er dem Vektor hinzugefügt wird. Ich erinnere mich, dass Visual Studio keine Variadic-Vorlagen unterstützt hat, aber mit Variadic-Vorlagen werden in Visual Studio 2013 RC unterstützt, also denke ich, dass eine konforme Signatur hinzugefügt wird.

Mit emplace_backwenn Sie die Argumente direkt an weiterleiten vector<Object>::value_type Konstruktor benötigen Sie keinen Typ, der verschiebbar oder kopierbar ist emplace_back Funktion, genau genommen. In dem vector<NonCopyableNonMovableObject> Fall ist dies nicht sinnvoll, da vector<Object>::value_type benötigt einen kopierbaren oder verschiebbaren Typ, um zu wachsen.

Aber Hinweis dass dies nützlich sein könnte std::map<Key, NonCopyableNonMovableObject>denn sobald Sie einen Eintrag in der Karte zugewiesen haben, muss dieser nicht mehr verschoben oder kopiert werden, im Gegensatz zu mit vectorwas bedeutet, dass Sie verwenden können std::map effektiv mit einem zugeordneten Typ, der weder kopierbar noch verschiebbar ist.

996320cookie-checkpush_back gegen emplace_back

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy