Efficienza di C ++ 11 push_back () con std :: move rispetto a emplace_back () per oggetti già costruiti


88

In C ++ 11 emplace_back()è generalmente preferito (in termini di efficienza) in push_back()quanto consente la costruzione sul posto, ma è ancora così quando si utilizzapush_back(std::move()) con un oggetto già costruito?

Ad esempio, è emplace_back()ancora preferito in casi come i seguenti?

Inoltre, nell'esempio precedente c'è qualche vantaggio nel fare invece:

o il trasferimento qui è del tutto ridondante o non ha alcun effetto?


myvector.emplace_back(mystring);copia e non si muove. Le altre due mosse e dovrebbero essere equivalenti.
TC

Risposte:


117

Vediamo cosa fanno le diverse chiamate che hai fornito:

  1. emplace_back(mystring): Questa è una costruzione sul posto del nuovo elemento con qualsiasi argomento fornito. Dato che hai fornito un valore l, quella costruzione sul posto in effetti è una costruzione di copia, cioè è lo stesso che chiamarepush_back(mystring)

  2. push_back(std::move(mystring)): Questo chiama l'inserimento della mossa, che nel caso di std::stringè una costruzione del movimento sul posto.

  3. emplace_back(std::move(mystring)): Anche questa è una costruzione sul posto con gli argomenti che hai fornito. Poiché quell'argomento è un valore, chiama il costruttore di mosse di std::string, cioè è una costruzione di mosse sul posto come in 2.

In altre parole, se chiamato con un argomento di tipo T, sia esso un rvalue o lvalue, emplace_backe push_backsono equivalenti.

Tuttavia, per qualsiasi altro argomento, emplace_backvince la gara, ad esempio con a char const*in a vector<string>:

  1. emplace_back("foo")chiede la std::string(char const*)costruzione sul posto.

  2. push_back("foo")prima deve richiedere std::string(char const*)la conversione implicita necessaria per abbinare la firma della funzione, e quindi un inserimento di spostamento come nel caso 2. sopra. Quindi è equivalente apush_back(string("foo"))


1
Il costruttore di spostamento è generalmente più efficiente dei costruttori di copia, quindi l'uso di rvalue (caso 2, 3) è più efficiente dell'uso di un lvalue (caso 1) nonostante la stessa semantica.
Ryan Li

che dire di una situazione del genere? void foo (string && s) {vector.emplace (s); // 1 vector.emplace (std :: move (s)); // 2}
VALOD9

@ VALOD quelli sono di nuovo i numeri 1 e 3 della mia lista. spuò essere definito come riferimento rvalue che si lega solo a rvalues, ma all'interno di foo, sè un lvalue.
Arne Mertz

1

Emplace_back ottiene un elenco di riferimenti rvalue e cerca di costruire un elemento contenitore direttamente sul posto. Puoi chiamare emplace_back con tutti i tipi supportati dai costruttori dell'elemento contenitore. Quando si chiama emplace_back per parametri che non sono riferimenti rvalue, "ricade" sui riferimenti normali e almeno il costruttore di copia viene chiamato quando il parametro e gli elementi contenitore sono dello stesso tipo. Nel tuo caso 'myvector.emplace_back (mystring)' dovrebbe fare una copia della stringa perché il compilatore non poteva sapere che il parametro myvector è mobile. Quindi inserisci std :: move ciò che ti dà il vantaggio desiderato. Il push_back dovrebbe funzionare come emplace_back per gli elementi già costruiti.


2
Questo è un modo interessante per descrivere inoltro / riferimenti universali.
TC
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.