Pregunta push_back vs emplace_back


Estoy un poco confundido con respecto a la diferencia entre push_back y emplace_back.

void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);

Como hay un push_back sobrecarga teniendo una referencia rvalue No veo muy bien cuál es el propósito de emplace_back se convierte?


521
2017-11-29 12:04


origen


Respuestas:


Además de lo que dijo el visitante:

La función void emplace_back(Type&& _Val) proporcionado por MSCV10 es no conforme y redundante, porque como notó es estrictamente equivalente a push_back(Type&& _Val).

Pero la forma real de C ++ 0x emplace_back es realmente útil: void emplace_back(Args&&...);

En lugar de tomar una value_type toma una lista variada de argumentos, por lo que significa que ahora puede reenviar perfectamente los argumentos y construir directamente un objeto en un contenedor sin ningún tipo de temporal.

Eso es útil porque no importa cuánta astucia RVO y semántica de movimientos traiga a la mesa todavía hay casos complicados en los que un push_back probablemente haga copias innecesarias (o se mueva). Por ejemplo, con el tradicional insert() función de un std::map, tienes que crear un temporal, que luego se copiará en un std::pair<Key, Value>, que luego se copiará en el mapa:

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);

Entonces, ¿por qué no implementaron la versión correcta de emplace_back en MSVC? En realidad, me molestó demasiado hace un tiempo, así que hice la misma pregunta en el Blog de Visual C ++. Aquí está la respuesta de Stephan T Lavavej, el mantenedor oficial de la implementación de la biblioteca estándar de Visual C ++ en Microsoft.

P: ¿Las funciones beta 2 emplace son solo algún tipo de marcador de posición en este momento?

A: como usted sabrá, plantillas variadas   no están implementados en VC10. Nosotros   simularlos con preprocesador   maquinaria para cosas como    make_shared<T>(), tupla, y el nuevo   cosas en <functional>. Esta   la maquinaria del preprocesador es relativamente   difícil de usar y mantener También,   afecta significativamente la compilación   velocidad, ya que tenemos que repetidamente   incluye subcabeceras. Debido a un   combinación de nuestras limitaciones de tiempo   y preocupaciones de velocidad de compilación,   no han simulado plantillas variadas   en nuestras funciones emplace

Cuando las plantillas variadic son   implementado en el compilador, puedes   esperamos que aprovechemos   ellos en las bibliotecas, incluso en   nuestras funciones emplace Nosotros tomamos   conformidad muy en serio, pero   desafortunadamente, no podemos hacer todo   de repente.

Es una decisión comprensible. Todos los que intentaron emular una plantilla variadic con horribles trucos del preprocesador saben lo desagradable que es esto.


423
2017-11-29 18:00



emplace_back no debería tomar un argumento de tipo vector::value_type, pero en cambio, los argumentos variados que se envían al constructor del elemento adjunto.

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

Es posible pasar un value_type que se reenviará al constructor de copia.

Debido a que reenvía los argumentos, esto significa que si no tiene el valor r, esto significa que el contenedor almacenará una copia "copiada", no una copia movida.

 std::vector<std::string> vec;
 vec.emplace_back(std::string("Hello")); // moves
 std::string s;
 vec.emplace_back(s); //copies

Pero lo anterior debe ser idéntico a lo que push_back hace. Probablemente sea más adecuado para casos de uso como:

 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

154
2017-11-29 12:47



Optimización para emplace_backse puede demostrar en el siguiente ejemplo.

por emplace_back constructor A (int x_arg) sera llamado. Y para push_back  A (int x_arg) se llama primero y move A (A &&rhs) se llama después.

Por supuesto, el constructor debe estar marcado explicit, pero para el ejemplo actual es bueno eliminar la explicitud.

#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;
}

salida:

call emplace_back:
A (x_arg)

call push_back:
A (x_arg)
A (A &&)

39
2017-10-27 16:42



emplace_back la implementación conforme remitirá los argumentos al vector<Object>::value_typeconstructor cuando se agrega al vector. Recuerdo que Visual Studio no admitió plantillas variadic, pero con Visual Studio 2013 RC se admitirán plantillas variad, así que supongo que se agregará una firma conforme.

Con emplace_back, si reenvía los argumentos directamente a vector<Object>::value_type constructor, no necesita un tipo para ser movible o copiable para emplace_back función, estrictamente hablando. En el vector<NonCopyableNonMovableObject> caso, esto no es útil, ya que vector<Object>::value_type  necesita un tipo copiable o móvil para crecer.

Pero Nota que esto podría ser útil para std::map<Key, NonCopyableNonMovableObject>, ya que una vez que asigna una entrada en el mapa, ya no necesita moverla ni copiarla, a diferencia de vector, lo que significa que puedes usar std::map efectivamente con un tipo mapeado que no se puede copiar ni mover.


7
2017-09-24 04:39



Una más en caso de listas:

// construye los elementos en su lugar.
emplace_back ("elemento");

// Creará un nuevo objeto y luego copiará (o moverá) su valor de argumentos. push_back (explicitDataType {"element"});


1
2017-12-28 10:08



Aquí se muestra un buen código para push_back y emplace_back.

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

Puede ver la operación de movimiento en push_back y no en emplace_back.


1
2018-02-27 14:26