Zusätzlich zu dem, was der Besucher gesagt hat:
Die Funktion void emplace_back(Type&& _Val)
, die von MSCV10 bereitgestellt wird, ist nicht konform und überflüssig, denn wie Sie bemerkt haben, ist sie streng äquivalent zu push_back(Type&& _Val)
.
Aber die echte C++0x-Form von emplace_back
ist wirklich nützlich: void emplace_back(Args&&...)
;
Anstatt einen value_type
zu übernehmen, nimmt sie eine variadische Liste von Argumenten entgegen, das bedeutet, dass Sie die Argumente jetzt perfekt weiterleiten und direkt ein Objekt in einem Container konstruieren können, ohne dass überhaupt ein temporäres Objekt entsteht.
Das ist nützlich, weil egal wie viel Cleverness RVO und Bewegungssemantik mitbringen, es immer noch komplizierte Fälle gibt, in denen ein push_back
wahrscheinlich unnötige Kopien (oder Bewegungen) verursacht. Zum Beispiel bei der traditionellen insert()
-Funktion einer std::map
, müssen Sie ein temporäres Objekt erstellen, das dann in ein std::pair
kopiert wird, das dann in die Karte kopiert wird:
std::map m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";
// drücke die Daumen, dass der Optimierer wirklich gut ist
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString)));
// sollte für den Optimierer einfacher sein
m.emplace(4, anInt, aDouble, aString);
Warum haben sie also nicht die richtige Version von emplace_back
in MSVC implementiert? Eigentlich hat mich das auch vor einer Weile 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 C++-Standardbibliotheksimplementierung bei Microsoft.
Q: Sind die Beta-2-emplace-Funktionen im Moment nur eine Art Platzhalter?
A: Wie Sie vielleicht wissen, sind variadische Vorlagen nicht in VC10 implementiert. Wir simulieren sie mit Präprozessormaschinerie für Dinge wie make_shared()
, tuple und die neuen Dinge in . Diese Präprozessormaschinerie ist relativ schwer zu benutzen und zu pflegen. Außerdem beeinflusst sie die Kompilierung stark, da wir immer wieder Unterheader einfügen müssen. Aufgrund von einer Kombination aus Zeitbeschränkungen und Kompilierungsgeschwindigkeitsbedenken haben wir keine variadischen Vorlagen simuliert in unseren emplace-Funktionen.
Wenn variadische Vorlagen im Compiler implementiert sind, können Sie erwarten, dass wir sie in den Bibliotheken nutzen, auch in unseren emplace-Funktionen. Wir nehmen Konformität sehr ernst, aber leider können wir nicht alles auf einmal erledigen.
Es ist eine verständliche Entscheidung. Jeder, der schon einmal versucht hat, variadische Vorlagen mit abscheulichen Präprozessor-Tricks nachzuahmen, weiß, wie widerlich dieser Kram wird.
13 Stimmen
Einige gute Lektüre hier: open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2642.pdf
19 Stimmen
Beachte, dass (wie Thomas unten sagt), der Code in der Frage stammt aus der Emulation von C++0x in MSVS, nicht was C++0x tatsächlich ist.
5 Stimmen
Ein besseres Papier zum Lesen wäre: open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2345.pdf. N2642 ist hauptsächlich die Formulierung für den Standard; N2345 ist das Papier, das die Idee erklärt und motiviert.
0 Stimmen
Bitte beachten Sie, dass auch in MSVC10 eine
template void emplace_back(_Valty&& _Val)
Version vorhanden ist, die eine Universalreferenz akzeptiert, die eine perfekte Weiterleitung anexplizite
einargumentige Konstruktoren ermöglicht.0 Stimmen
Verwandte Frage: Gibt es einen Fall, wo
push_back
gegenüberemplace_back
bevorzugt wird? Der einzige Fall, an den ich denken kann, ist, wenn eine Klasse auf irgendeine Weise kopierbar wäre (T&operator=(constT&)
), aber nicht konstruierbar (T(constT&)
), aber ich kann nicht verstehen, warum man das jemals wollen würde.0 Stimmen
Auch siehe dies.
0 Stimmen
@Ben Alle anderen (wenn ich es mir erlauben darf) interpretieren ein nicht qualifiziertes "kopierbar" als kopierbar, nicht als kopierzuweisbar wie du gezeigt hast. Sowohl
emplace_back()
als auchpush_back()
werden den Kopierkonstruktor aufrufen - d.h. beide führen eine Konstruktion durch, d.h. keine führt eine Zuweisung durch - so dass deine vorgeschlagene Klasse damit auch nicht besser abschneiden würde.0 Stimmen
Der verwandte Fall, über den ich gestolpert bin (den ich gerade in einem separaten Thread auf dieser Seite erwähnt habe), ist, dass
emplace_back
"implizit"explicit
Konstruktoren aufrufen kann, was zu unerwünschtem Verhalten führen könnte. (Für schwere Klassen mache ich oft den Kopierkonstruktorexplicit
und lasse den Move-Konstruktor implizit.v.emplace_back(x)
hat mich erwischt, indem es im Grunde genommenv.emplace_back(X(x))
gemacht hat, als ich gehofft hatte, dass es einen Fehler auslöst und mich zwingt,v.push_back(std::move(x))
zu schreiben.)0 Stimmen
Ausgezeichnet und leicht verständliche Antwort hier: quora.com/…
0 Stimmen
Verwandt: Warum sollte ich jemals push_back anstelle von emplace_back verwenden?
0 Stimmen
Zwei Artikelreihen zu diesem Thema: medium.com/@its.me.siddh/… medium.com/@its.me.siddh/…