Pregunta

Actualizar:Ejemplo de código editado para usar AutoA como solución alternativa (que era la intención original).Me di cuenta de esto después de ver la respuesta de rlbond.

Estoy tratando de incorporar el uso de auto_ptr en mi código basado en recomendaciones de este hilo:

Expresar el uso de argumentos de C++ a través de interfaces de métodos.

Sin embargo, recibo algunos errores de compilación inesperados al compilar con Visual Studio 6.0.Tiene un problema cuando se trata de asignaciones/copias de un std::auto_ptr de un tipo derivado a un std::auto_ptr del tipo base.¿Es este un problema específico de mi compilador?

Sé que hay una fuerte recomendación de usar Boost, pero en mi proyecto no es una opción.Si todavía quiero usar auto_ptr, ¿me veo obligado a utilizar la solución alternativa de llamar? std::auto_ptr::release()?Por lo que he encontrado hasta ahora, este problema resulta en un error del compilador, por lo que es bastante fácil de detectar.Sin embargo, ¿adoptar la convención de llamar a release para asignar a un 'auto_ptr' de tipo base podría exponerme a problemas de mantenimiento?Especialmente si se construye con un compilador diferente (suponiendo que otros compiladores no tengan este problema).

Si el release() La solución alternativa no es buena debido a mis circunstancias, ¿debería recurrir a una convención diferente para describir la transferencia de propiedad?

El siguiente es un ejemplo que ilustra el problema.

#include "stdafx.h"
#include <memory>

struct A
{
    int x;
};

struct B : public A
{
    int y;
};

typedef std::auto_ptr<A> AutoA;
typedef std::auto_ptr<B> AutoB;

void sink(AutoA a)
{
    //Some Code....
}

int main(int argc, char* argv[])
{
    //Raws to auto ptr
    AutoA a_raw_to_a_auto(new A());
    AutoB b_raw_to_b_auto(new B());
    AutoA b_raw_to_a_auto(new B());

    //autos to same type autos
    AutoA a_auto_to_a_auto(a_raw_to_a_auto);
    AutoB b_auto_to_b_auto(b_raw_to_b_auto);

    //raw derive to auto base
    AutoB b_auto(new B());

    //auto derive to auto base
    AutoA b_auto_to_a_auto(b_auto);  //fails to compile

    //workaround to avoid compile error.
    AutoB b_workaround(new B());
    AutoA b_auto_to_a_auto_workaround(b_workaround.release());

    sink(a_raw_to_a_auto);
    sink(b_raw_to_b_auto);  //fails to compile

    return 0;
}

Error de compilación:

Compiling...
Sandbox.cpp
C:\Program Files\Microsoft Visual Studio\MyProjects\Sandbox\Sandbox.cpp(40) : error C2664: '__thiscall std::auto_ptr<struct A>::std::auto_ptr<struct A>(struct A *)' : cannot convert parameter 1 from 'class std::auto_ptr<struct B>' to 'struct A *'
        No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
C:\Program Files\Microsoft Visual Studio\MyProjects\Sandbox\Sandbox.cpp(47) : error C2664: 'sink' : cannot convert parameter 1 from 'class std::auto_ptr<struct B>' to 'class std::auto_ptr<struct A>'
        No constructor could take the source type, or constructor overload resolution was ambiguous
Error executing cl.exe.

Sandbox.exe - 2 error(s), 0 warning(s)
¿Fue útil?

Solución

El primero es facil:

AutoA b_auto_to_a_auto(b_auto);  //fails to compile

Esto falla en VC6 ya que requiere plantillas de funciones miembro, algo que la biblioteca estándar de VC6 no admite.Sin embargo, se compila en compiladores compatibles con los estándares.

Solución alterna:

AutoA b_auto_to_a_auto( b_auto.release() );

El segundo es mucho más sutil :)

sink(b_raw_to_b_auto);  //fails to compile

Este no debería compilarse en un compilador que cumpla con los estándares, porque hay una conversión implícita.El compilador convierte lo anterior en

sink( std::auto_ptr<A>( b_raw_to_b_auto ) );

sin embargo, sink acepta std::auto_ptr<A> por valor, entonces el temporal std::auto_ptr<A> creado implícitamente por el compilador debe ser copia construida en el argumento para sink.Ahora, los temporales como ese son valores.Los valores R no se vinculan a referencias no constantes, pero std::auto_ptrEl "constructor de copia" toma su argumento por no constante referencia.

Ahí lo tienes: error de compilación.AFAICS se trata de un comportamiento conforme a las normas.La "semántica de movimiento" de C++-0x solucionará ese problema agregando un "constructor de copia" que toma una referencia de valor, aunque no estoy seguro de cuánto amor std::auto_ptr seguiremos recibiendo en el futuro, con lo que std::shared_ptr y todo.

Solución alternativa para el segundo:

AutoA tmp( b_raw_to_b_auto/*.release() for VC6*/ );
sink( tmp );

Otros consejos

AutoA b_auto_to_a_auto(b_auto);  //fails to compile

sink(b_raw_to_b_auto);  //fails to compile

Pavel Minaev señala algo que realmente no sabía:

La primera llamada debe compilar, porque hay una conversión implícita de una B* a una A*. Sin embargo, el segundo no se compilará. La siguiente voluntad:

sink(static_cast<AutoA>(b_raw_to_b_auto));

VC6 es conocido por no ser muy bueno con las plantillas.

Le sugiero que actualice su base de código a uno que realmente funcione y comience a usar técnicas RAII, especialmente boost::shared_ptr. Sé que dices que no puedes, y sé que es difícil, pero prácticamente no tendrás fugas de memoria y muchos, muchos menos errores.

Por otra parte, tal vez incluso sin una funcionalidad completa que pueda usar auto_ptr?

Hay dos problemas aquí. En primer lugar, esto:

AutoA b_auto_to_a_auto(b_auto);  

Está Cumplimiento de forma perfectamente estándar y debería compilar. Déjame explicar por qué. El estándar ISO C ++ especifica (20.4.5.1 [lib.auto.ptr.cons]/4-6) el siguiente constructor para auto_ptr<X> (entre otros);

template<class Y> auto_ptr(auto_ptr<Y>&) throw();

Tenga en cuenta que Y es un tipo diferente de X aquí. Y el estándar además dice:

Requiere: Y* puede convertirse implícitamente en x*.

Lo único a lo que se debe prestar atención aquí es que el argumento del constructor es una referencia para no constantemente. Para su caso, esto no es un problema (ya que está pasando una variable no es tan convincente allí), pero se vuelve importante para la siguiente parte. Para concluir aquí: lo que estás viendo es comportamiento no estándar en VC6. Eso debería compilar en un compilador compatible (y compilará en VC7 y superior). Ahora en la segunda cosa:

sink(b_raw_to_b_auto);  //fails to compile

Este es realmente explicado perfectamente por MMutz, por lo que no entraré en detalles aquí; vea su respuesta. Para concluir sobre eso: Sí, esta línea no debería compilar, y no lo hará en un compilador que cumple (o VC6, como has descubierto).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top