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.
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.
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
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_back
A (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 &&)
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.
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.
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"});