Llamada inesperada al constructor por defecto al usar la semántica de movimiento.
Tengo dos piezas de código similares. La primera versión llama inesperadamente al constructor predeterminado mientras que la segunda no lo hace. Ambas llaman al operador de movimiento / constructor de movimiento, respectivamente, como se esperaba.
class MyResource
{
public:
MyResource() : m_data(0) { std::cout << "Constructor Predeterminado" << std::endl; }
MyResource(int data) : m_data(data) { std::cout << "Constructor con Entero" << std::endl; }
MyResource(MyResource const& other) = delete;
MyResource& operator=(MyResource const& other) = delete;
MyResource(MyResource&& other) noexcept : m_data(other.m_data) { std::cout << "Constructor de Movimiento" << std::endl; }
MyResource& operator=(MyResource&& other) noexcept { std::cout << "Operador de Movimiento" << std::endl; m_data = other.m_data; return *this; }
~MyResource() { std::cout << "Destructor" << std::endl; }
private:
int m_data = 0;
};
class MyWrapper
{
public:
MyWrapper(MyResource&& resource)
// : m_resource(std::move(resource)) // Versión 2
{
// m_resource = std::move(resource); // Versión 1
}
private:
MyResource m_resource;
};
Mi uso de prueba es:
MyWrapper* wrapper = new MyWrapper(MyResource(1));
delete wrapper;
Con la Versión 1, obtengo:
> Constructor con Entero
> Constructor Predeterminado
> Operador de Movimiento
> Destructor
> Destructor
Mientras que la Versión 2 muestra:
> Constructor con Entero
> Constructor de Movimiento
> Destructor
> Destructor
¿Cuál es la razón detrás de esta diferencia?
¿Por qué la Versión 1 llama al constructor predeterminado?
davy.ai
La razón de esta diferencia es que en la Versión 1, la variable miembro
m_resource
se inicializa por defecto antes de llamar al operador de movimiento. Esto se debe a que el cuerpo del constructor ya se está ejecutando en el momento en que se llama al operador de movimiento, y la inicialización por defecto ocurre antes de cualquier inicialización explícita en el cuerpo del constructor.En la Versión 2, la variable miembro
m_resource
se inicializa directamente mediante los parámetros del constructor de movimiento, por lo que no se necesita una inicialización por defecto.Para evitar la inicialización por defecto en la Versión 1, puedes usar la sintaxis de la lista de inicialización de miembros para inicializar directamente
m_resource
constd::move(resource)
, como en la Versión 2.