push_back emplace_back c++ c++11 c++17 emplace

c++ - push_back - ¿La mejor manera de usar emplace_back para evitar mover la llamada al constructor?



c++ emplace_back (1)

Parece que el constructor de movimiento todavía se llama aunque un prvalue se pasa directamente a emplace_back

Crees que lo haces, pero no lo pasas a la función. Le da un prvalue como argumento, sí, pero lo que emplace_back acepta es un paquete de referencias de reenvío . Una referencia debe referirse a un objeto, por lo que un temporal se materializa y se mueve.

La forma correcta de usar emplace_back es pasarle los argumentos para inicializar el objeto en su lugar . De esa manera, no necesita mover el tipo de elemento del vector (aunque puede que necesite mover / copiar los argumentos).

Acabo de enterarme de la resolución de copias garantizada en C ++ 17 . Según la respuesta a esa pregunta:

Cuando return T(); , esto inicializa el valor de retorno de la función a través de un prvalue . Como esa función devuelve T, no se crea ningún temporal; la inicialización del prvalue simplemente inicializa directamente el valor de retorno.

Lo que hay que entender es que, dado que el valor de retorno es un prvalue , aún no es un objeto. Es simplemente un inicializador para un objeto, al igual que T() es.

Así que me preguntaba, ¿esto funciona para algo más que:

T f() {return T();} T t = f();

Así que escribí este código con emplace_back para probarlo:

#include <vector> #include <iostream> struct BigObj{ BigObj() = default; BigObj(int) { std::cout << "int ctor called" << std::endl; } BigObj(const BigObj&){ std::cout << "copy ctor called" << std::endl; } BigObj(BigObj&&){ std::cout << "move ctor called" << std::endl; } }; BigObj f(){ return BigObj(2); } int g(){ return 2; } int main(){ std::vector<BigObj> v; v.reserve(10); std::cout << "emplace_back with rvalue /n"; v.emplace_back(1+1); std::cout << "emplace_back with f()/n"; v.emplace_back(f()); std::cout << "emplace_back with g()/n"; v.emplace_back(g()); }

Esta es la salida que obtengo (con elision de copia desactivada):

emplace_back with rvalue int ctor called emplace_back with f() int ctor called move ctor called emplace_back with g() int ctor called

Parece que el constructor de movimiento aún se llama, aunque un prvalue se pasa directamente a emplace_back , que pensé que podría usarse para construir directamente un objeto en lugar de usarlo para construir un temporal y luego moverlo.

¿Existe una forma más elegante de evitar la llamada al constructor de movimientos con emplace_back no sea hacer algo como lo que hace la función g() ?