es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Por qué no puedo devolver un objeto que tiene un constructor de movimiento pero cuyo constructor de copia está explícitamente eliminado?

Tengo un functor cuya declaración se ve así:

class logger {
public:
    log_t operator() (unsigned _LogLevel) {
        return log_t{_LogLevel};
    }
};

En el código, quiero que este functor construya y devuelva un objeto log_t. Pero el compilador se queja de que se ha eliminado el constructor de copia del objeto log_t.

Debo admitir que la clase log_t no tiene un constructor de copia, solo tiene un constructor de movimiento explícito y un constructor ordinario explícito con un parámetro. Debido a que log_t se deriva de la clase de plantilla de la biblioteca estándar basic_ostream y su constructor de copia ha sido eliminado explícitamente, solo puedo proporcionar un constructor de movimiento para objetos log_t.

Además, este objeto no puede usar static para devolver una referencia debido a los requisitos de su ciclo de vida.

He agregado -std=c++11 a las opciones de compilación. En mi opinión, independientemente de si se activa o no la opción RVO, según el estándar C++11, el valor de retorno de una función debe dar prioridad a llamar al constructor de movimiento en lugar del constructor de copia. En esta función de imitación, no debería ser necesario el constructor de copia, pero en realidad informa un error.

#include <iosfwd>
#include <ostream>    // para la clase de plantilla 'basic_ostream'

class mlog_t 
  : public std::basic_ostream
   <char,></char,><char> > {

public:
    explicit   mlog_t (unsigned) noexcept{}
               mlog_t (mlog_t&&) noexcept{}
               mlog_t (const mlog_t&) = delete;
              ~mlog_t () noexcept{}
    mlog_t& operator= (const mlog_t&) = delete;

private:
   /*
    *  Este es el puntero al búfer.
    */
    mutable void* instance_ptr;
};

class _mcl_mlog_t {
public:
    mlog_t operator() (unsigned _LogLevel) noexcept{
        return mlog_t{_LogLevel};
    }
};

_mcl_mlog_t mlog;

int main (){
    return 0;
}

Obtengo un error de compilación:

25 32 C:\Users\test.cpp [Error] uso de la función eliminada ‘mlog_t::mlog_t(const mlog_t&)’

He intentado la siguiente respuesta pero fallé:
“Para aprovechar esta característica de C++11, el constructor (en este caso tomando int) debe ser no explícito”.
(
https://stackoverflow.com/questions/7935639/can-we-return-objects-having-a-deleted-private-copy-move-constructor-by-value-fr )

¿Cuál es la razón de este error? ¿Cómo puedo resolver este problema?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    La razón del error se debe a que el constructor de copia de la clase mlog_t ha sido eliminado explícitamente, y la declaración de retorno en la función operator() requiere un constructor de copia.

    Una solución a este problema sería utilizar la función std::move para devolver el objeto como un rvalor, lo cual llamaría al constructor de movimiento en lugar del constructor de copia eliminado. Esto se puede hacer modificando la función operator() de la siguiente manera:

    mlog_t operator() (unsigned _LogLevel) noexcept {
        return std::move(mlog_t{_LogLevel});
    }
    

    Al utilizar std::move, indicamos que estamos transfieriendo la propiedad del objeto mlog_t al llamador de la función, y así se puede utilizar el constructor de movimiento en lugar del constructor de copia eliminado.

    Otra solución sería proporcionar un constructor de copia personalizado para la clase mlog_t que realice una operación de movimiento en lugar de una copia real. Esto se puede hacer de la siguiente manera:

    mlog_t(mlog_t const &other) noexcept : basic_ostream<char>{nullptr} {
        // Realizar una operación de movimiento en lugar de una copia real
        *this = std::move(const_cast<mlog_t&>(other));
    }
    

    Con este constructor, cualquier intento de copiar un objeto mlog_t realmente resultaría en una operación de movimiento, lo cual evitaría el uso del constructor de copia eliminado.

    Sin embargo, generalmente se recomienda utilizar std::move para transferir la propiedad de objetos en lugar de sobrecargar constructores de copia para realizar operaciones de movimiento, ya que esto puede resultar confuso y violar el principio de menor sorpresa para otros desarrolladores que podrían estar trabajando con el código.</mlog_t&>

Comments are closed.