Ik ben een beetje in de war over het verschil tussen push_back
en emplace_back
.
void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);
Aangezien er een push_back
overbelasting is bij het nemen van een rvalue-referentie, begrijp ik niet helemaal wat het doel van emplace_back
wordt?
Antwoord 1, autoriteit 100%
In aanvulling op wat de bezoeker zei:
De functie void emplace_back(Type&& _Val)
geleverd door MSCV10 is niet-conform en overbodig, omdat het, zoals u opmerkte, strikt equivalent is aan push_back(Type&& _Val)
.
Maar de echte C++0x-vorm van emplace_back
is erg handig: void emplace_back(Args&&...)
;
In plaats van een value_type
te nemen, is er een variadische lijst met argumenten nodig, dus dat betekent dat je de argumenten nu perfect kunt doorsturen en direct een object in een container kunt construeren zonder enige tijdelijke.
Dat is handig, want hoeveel slimheid RVO en semantiek ook brengen, er zijn nog steeds gecompliceerde gevallen waarin een push_back waarschijnlijk onnodige kopieën (of verplaatsing) maakt. Met de traditionele functie insert()
van een std::map
moet u bijvoorbeeld een tijdelijk bestand maken, dat vervolgens wordt gekopieerd naar een std::pair<Key, Value>
, die vervolgens naar de kaart wordt gekopieerd :
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);
Dus waarom hebben ze niet de juiste versie van emplace_back in MSVC geïmplementeerd? Eigenlijk zat ik er een tijdje geleden ook mee, dus ik stelde dezelfde vraag op de Visual C++ blog. Hier is het antwoord van Stephan T Lavavej, de officiële beheerder van de Visual C++ standaard bibliotheekimplementatie bij Microsoft.
V: Zijn bèta 2-emplace-functies op dit moment slechts een soort tijdelijke aanduiding?
A: Zoals u wellicht weet, variadische sjablonen
zijn niet geïmplementeerd in VC10. We
simuleer ze met preprocessor
machines voor dingen als:
make_shared<T>()
, tuple en de nieuwe
dingen in<functional>
. Dit
preprocessormachines zijn relatief
moeilijk in gebruik en onderhoud. Ook,
het heeft een aanzienlijke invloed op de compilatie
snelheid, zoals we herhaaldelijk moeten doen
ondertitels opnemen. Door een
combinatie van onze tijdsdruk
en compilatiesnelheid betreft, we
hebben geen variadische sjablonen gesimuleerd
in onze emplace-functies.Als variadische sjablonen zijn
geïmplementeerd in de compiler, kunt u:
verwachten dat we zullen profiteren van
ze in de bibliotheken, ook in
onze emplace-functies. We nemen
conformiteit zeer serieus, maar
helaas kunnen we niet alles doen
allemaal tegelijk.
Het is een begrijpelijke beslissing. Iedereen die slechts één keer heeft geprobeerd om een variadische sjabloon na te bootsen met vreselijke preprocessor-trucs, weet hoe walgelijk dit spul wordt.
Antwoord 2, autoriteit 33%
emplace_back
mag geen argument van het type vector::value_type
aannemen, maar in plaats daarvan variadische argumenten die worden doorgestuurd naar de constructor van het toegevoegde item.
template <class... Args> void emplace_back(Args&&... args);
Het is mogelijk om een value_type
door te geven die wordt doorgestuurd naar de kopieerconstructor.
Omdat het de argumenten doorstuurt, betekent dit dat als je geen rvalue hebt, dit nog steeds betekent dat de container een “gekopieerde” kopie opslaat, geen verplaatste kopie.
std::vector<std::string> vec;
vec.emplace_back(std::string("Hello")); // moves
std::string s;
vec.emplace_back(s); //copies
Maar het bovenstaande moet identiek zijn aan wat push_back
doet. Het is waarschijnlijk eerder bedoeld voor gebruikssituaties zoals:
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
Antwoord 3, autoriteit 17%
Optimalisatie voor emplace_back
kan in het volgende voorbeeld worden gedemonstreerd.
Voor emplace_back
wordt de constructor A (int x_arg)
aangeroepen. En voor
push_back
A (int x_arg)
wordt eerst aangeroepen en move A (A &&rhs)
wordt daarna aangeroepen.
Natuurlijk moet de constructor worden gemarkeerd als explicit
, maar voor het huidige voorbeeld is het goed om de explicietheid te verwijderen.
#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;
}
uitvoer:
call emplace_back:
A (x_arg)
call push_back:
A (x_arg)
A (A &&)
Antwoord 4, autoriteit 2%
Nog een voorbeeld voor lijsten:
// constructs the elements in place.
emplace_back("element");
// creates a new object and then copies (or moves) that object.
push_back(ExplicitDataType{"element"});
Antwoord 5
Een mooie code voor de push_back en emplace_back wordt hier getoond.
http://en.cppreference.com/w/cpp/container/ vector/emplace_back
Je kunt de verplaatsingsbewerking zien op push_back en niet op emplace_back.
Antwoord 6
emplace_back
-conforme implementatie zal argumenten doorsturen naar de vector<Object>::value_type
constructor wanneer deze aan de vector wordt toegevoegd. Ik herinner me dat Visual Studio geen variadische sjablonen ondersteunde, maar dat variadische sjablonen worden ondersteund in Visual Studio 2013 RC, dus ik denk dat er een overeenkomstige handtekening zal worden toegevoegd.
Met emplace_back
, als u de argumenten rechtstreeks doorstuurt naar de vector<Object>::value_type
constructor, heeft u geen type nodig om verplaatsbaar of kopieerbaar te zijn voor emplace_back
functie, strikt genomen. In het geval vector<NonCopyableNonMovableObject>
is dit niet nuttig, aangezien vector<Object>::value_type
een kopieerbaar of verplaatsbaar type nodig heeft om te groeien.
Maar houd er rekening meedat dit nuttig kan zijn voor std::map<Key, NonCopyableNonMovableObject>
, aangezien als u eenmaal een item op de kaart hebt toegewezen, dit niet nodig is nooit meer worden verplaatst of gekopieerd, in tegenstelling tot vector
, wat betekent dat u std::map
effectief kunt gebruiken met een toegewezen type dat noch kopieerbaar noch verplaatsbaar is.
Antwoord 7
Specifieke use case voor emplace_back
: als u een tijdelijk object moet maken dat vervolgens in een container wordt gepusht, gebruikt u emplace_back
in plaats van push_back
. Het maakt het object op zijn plaats in de container.
Opmerkingen:
push_back
zal in het bovenstaande geval een tijdelijk object maken en verplaatsen
in de container. Echter, in-place constructie gebruikt vooremplace_back
zou meer zijn
performanter dan het construeren en vervolgens verplaatsen van het object (wat over het algemeen wat kopiëren met zich meebrengt).- Over het algemeen kun je in alle gevallen zonder veel problemen
emplace_back
gebruiken in plaats vanpush_back
. (Zie uitzonderingen)