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 unprvalue
. Como esa función devuelve T, no se crea ningún temporal; la inicialización delprvalue
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 queT()
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()
?