2009-04-16 9 views
9

me encontré con un error del compilador que no tiene mucho sentido para mí:Por qué no auto_ptr trabajos de construcción utilizando la sintaxis =

#include <memory> 
using namespace std; 

auto_ptr<Table> table = db->query("select * from t"); 

error: la conversión de 'Tabla *' de tipo no escalar ' std :: auto_ptr < Tabla>' pidió

Sin embargo, el trabajo siguiente línea hace:

auto_ptr<Table> table(db->query("select * from t")); 

¿Qué hay en esta definiton del constructor que hace que no funcione como se esperaba? Pensé que las declaraciones inicializadas usaban los constructores.

Aquí está mi auto_ptr 's constructor (de la SGI STL):

explicit 
auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { } 

Respuesta

17

Es la palabra clave "explícita".

template <typename T> 
struct foo 
{ 
    explicit foo(T const *) 
    { 
    } 
}; 


template <typename T> 
struct bar 
{ 
    bar(T const *) 
    { 
    } 
}; 


int main(int argc, char **argv) 
{ 
    int a; 
    foo<int> f = &a; // doesn't work 
    bar<int> b = &a; // works 
} 

La palabra clave "explicit" impide que el constructor se utilice para las conversiones de tipo implícito. Tenga en cuenta los siguientes dos prototipos de funciones:

void baz(foo<int> const &); 
void quux(bar<int> const &); 

Con estas definiciones, intente llamar a las dos funciones con un puntero int:

baz(&a); // fails 
quux(&a); // succeeds 

En el caso de quux, el puntero int se convierte implícitamente a un bar.

EDIT: Para ampliar lo que otras personas comentaron, considere la (bastante tonto) siguiente código:

void bar(std::auto_ptr<int>); 


int main(int argc, char **argv) 
{ 
    bar(new int()); // probably what you want. 

    int a; 
    bar(&a); // ouch. auto_ptr would try to delete a at the end of the 
      // parameter's scope 

    int * b = new int(); 
    bar(b); 
    *b = 42; // more subtle version of the above. 
} 
+0

Ugh. Justo cuando me convenzo a mí mismo de que he encontrado un uso para auto_ptr, su diseño me golpea en la cara. Volver a ir viejo eliminar. –

+2

No deje caer auto_ptr solo por eso. Simplemente use el constructor explícito: auto_ptr

tabla (db-> query ("select * from t")); O mueva el shared_ptr. – Eclipse

+1

Hay una muy buena razón para que el constructor sea explícito. Lo protege de errores accidentales. Es bastante fácil poner el auto_ptr (...) alrededor. – lothar

8

Es necesario utilizar

auto_ptr<Table> table = auto_ptr<Table>(db->query("select * from t")); 

auto_ptr no define un operador de asignación por su tipo de plantilla. La única asignación permitida es de otro auto_ptr (y su constructor del puntero es explícito). Esto se hace para proteger el uso indebido accidental de auto_ptr, ya que auto_ptr asume la propiedad de la memoria.

Mi conjetura es que se necesita la forma de asignación de utilizar múltiples consultas tras otro como:

// initialize using constructor 
auto_ptr<Table> table(db->query("select * from t1")); 
... 
// new query using assignment 
table = auto_ptr<Table>(db->query("select * from t2")); 
... 
// another query using assignment 
table = auto_ptr<Table>(db->query("select * from t3")); 
+1

'¿Por qué no auto_ptr

mesa (db-> query ("SELECT * FROM t"));'? – avakar

+0

Bueno, pensó que tú mismo (mira su pregunta). Y el formulario de asignación es necesario cada vez que haces más de una consulta tras otra :-) – lothar

+0

Ojalá hubiera una forma de editar comentarios. usted - maldita escritura rápida ;-) – lothar

2

Agregando a lo Lothar dijeron: Porque el constructor auto_ptr se declara con la palabra clave explicit, se necesita usar un molde explícito para crear un auto_ptr desde un puntero sin formato. (Antes de la introducción de explicit, la conversión implícita era la perdición de muchos desarrolladores de C++ nuevos y experimentados)

5

El constructor se declara explícito, lo que significa que no se utilizará para la conversión de tipo implícito. La conversión implícita a auto_ptr podría llevar fácilmente a situaciones indeseables ya que auto_ptr toma posesión del puntero.

Por ejemplo, si auto_ptr permitiría la conversión implícita de un puntero y accidentalmente pasó un puntero a un método que toma un auto_ptr el puntero se convertiría silenciosamente a un auto_ptr y posteriormente se eliminaría cuando la función finaliza, incluso si eso no fuera así. la intención.Pero al marcar el constructor como una conversión explícita ya no puede suceder en silencio y al llamar al constructor, usted expresa claramente la intención de pasar la propiedad al auto_ptr, evitando así cualquier posible confusión.

void fun(std::auto_ptr<Foo> foo) // Assume implicit conversion is allowed. 
{ 
    // do stuff with foo 
} 

Foo *foo = new Foo(); 

f(foo); // Normally this isn't allowed. 

foo->bar(); // Oops 
Cuestiones relacionadas