it-swarm.com.de

push_back vs emplace_back

Ich bin etwas verwirrt über den Unterschied zwischen 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 -Überlastung gibt, die eine R-Wert-Referenz aufnimmt, sehe ich nicht ganz, wozu emplace_back dient.

650
ronag

Zusätzlich zu dem, was der Besucher sagte:

Die von MSCV10 bereitgestellte Funktion void emplace_back(Type&& _Val) ist nicht konform und redundant, da sie, wie Sie festgestellt haben, der Funktion Push_back(Type&& _Val) genau entspricht.

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

Anstatt einen value_type zu nehmen, werden verschiedene Argumente aufgelistet, sodass Sie die Argumente jetzt perfekt weiterleiten und ein Objekt direkt in einen Container konstruieren können, ohne dass ein temporäres Objekt erforderlich ist.

Dies ist nützlich, da unabhängig von der Cleverness, die RVO und die Bewegungssemantik auf den Tisch bringen, immer noch komplizierte Fälle auftreten, in denen ein Push_back möglicherweise unnötige Kopien (oder Bewegungen) erstellt. Zum Beispiel müssen Sie mit der traditionellen Funktion insert() eines std::map eine temporäre Datei erstellen, die dann in einen std::pair<Key, Value> kopiert wird, der dann in die Map kopiert wird:

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 nicht die richtige Version von emplace_back in MSVC implementiert? Eigentlich hat es mich vor einiger Zeit auch gestört, also habe ich die gleiche Frage im Visual C++ - Blog gestellt. Hier ist die Antwort von Stephan T Lavavej, dem offiziellen Betreuer der Visual C++ - Standardbibliotheksimplementierung bei Microsoft.

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

A: Wie Sie vielleicht wissen, sind in VC10 keine unterschiedlichen Vorlagen implementiert. Wir simulieren sie mit Präprozessor-Maschinen für Dinge wie make_shared<T>(), Tuple und die neuen Dinge in <functional>. Diese Präprozessormaschinerie ist relativ schwierig zu bedienen und zu warten. Außerdem wirkt sich dies erheblich auf die Kompilierungsgeschwindigkeit aus, da immer wieder Unterüberschriften eingefügt werden müssen. Aufgrund einer Kombination aus Zeitbeschränkungen und Problemen mit der Kompilierungsgeschwindigkeit haben wir in unseren Arbeitsplatzfunktionen keine variablen Vorlagen simuliert.

Wenn verschiedene Vorlagen im Compiler implementiert sind, können Sie davon ausgehen, dass wir sie in den Bibliotheken nutzen, einschließlich unserer Funktionen für den Arbeitsplatz. Wir nehmen die Konformität sehr ernst, aber leider können wir nicht alles auf einmal tun.

Es ist eine verständliche Entscheidung. Jeder, der nur ein einziges Mal versucht hat, ein variables Template mit schrecklichen Tricks des Präprozessors zu emulieren, weiß, wie ekelhaft dieses Zeug wird.

504
Thomas Petit

emplace_back sollte kein Argument vom Typ vector::value_type annehmen, sondern verschiedene Argumente, die an den Konstruktor des angehängten Elements weitergeleitet werden.

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

Es ist möglich, einen value_type zu übergeben, der an den Kopierkonstruktor weitergeleitet wird.

Da die Argumente weitergeleitet werden, bedeutet dies, dass der Container eine "kopierte" Kopie und keine verschobene Kopie speichert, wenn Sie keinen Wert haben.

 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 sein mit dem, 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
182
visitor

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

Für emplace_back wird der Konstruktor A (int x_arg) aufgerufen. Und für Push_backA (int x_arg) wird zuerst und danach move A (A &&rhs) aufgerufen.

Natürlich muss der Konstruktor als explicit markiert sein, aber für das aktuelle Beispiel ist es gut, explizite Aussagen 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;
}

ausgabe:

call emplace_back:
A (x_arg)

call Push_back:
A (x_arg)
A (A &&)
61
vadikrobot

Die emplace_back -konforme Implementierung leitet Argumente an den vector<Object>::value_type -Konstruktor weiter, wenn sie zum Vektor hinzugefügt werden. Ich erinnere mich, dass Visual Studio keine variadischen Vorlagen unterstützt hat, aber mit variadischen Vorlagen wird Visual Studio 2013 RC unterstützt, daher wird vermutlich eine entsprechende Signatur hinzugefügt.

Wenn Sie mit emplace_back die Argumente direkt an den Konstruktor vector<Object>::value_type weiterleiten, muss ein Typ für die Funktion emplace_back nicht unbedingt verschiebbar oder kopierbar sein. Im Fall von vector<NonCopyableNonMovableObject> ist dies nicht sinnvoll, da vector<Object>::value_type einen kopierbaren oder beweglichen Typ benötigt, um zu wachsen.

Aber beachte dass dies nützlich sein könnte für std::map<Key, NonCopyableNonMovableObject>, da ein einmal zugewiesener Eintrag in der Map nicht mehr verschoben oder kopiert werden muss, im Gegensatz zu vector, was bedeutet, dass Sie std::map effektiv mit einem zugeordneten Typ verwenden können, der weder kopierbar noch verschiebbar ist.

8
Germán Diago

Ein Nizza Code für das Push_back und das emplace_back wird hier gezeigt.

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

Sie können den Verschiebevorgang bei Push_back und nicht bei emplace_back sehen.

6
Dharma

Eine weitere bei Listen:

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


//It will create new object and then copy(or move) its value of arguments.
Push_back(explicitDataType{"element"});
5
user3847351