2009-04-16 5 views
9

je suis tombé sur une erreur du compilateur qui n'a pas fait sens pour moi:Pourquoi ne pas auto_ptr Travaux de construction en utilisant = syntaxe

#include <memory> 
using namespace std; 

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

erreur: la conversion de « Table * » type non scalaire ' std :: auto_ptr < Table> » a demandé

Cependant, la ligne suivante fonctionne:

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

Qu'est-ce sur ce definiton du constructeur qui l'empêche de travailler comme je l'attends? Je pensais que les déclarations initialisées utilisaient les constructeurs.

Voici mon constructeur d » auto_ptr (de la STL SGI):

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

Répondre

17

Il est le mot-clé « explicite ».

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 
} 

Le mot clé "explicit" empêche le constructeur d'être utilisé pour les conversions de type implicite. Considérons les deux prototypes fonctionnels suivants:

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

Avec ces définitions, essayez d'appeler les deux fonctions avec un pointeur int:

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

Dans le cas de quux, votre pointeur int a été implicitement converti en un bar.

EDIT: Pour développer ce que les autres personnes ont commenté, considérer les points suivants (plutôt ridicule) Code:

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. Juste au moment où je me convaincs que j'ai trouvé une utilisation pour auto_ptr, son design me frappe au visage. Retour pour aller effacer –

+2

Ne laissez pas tomber auto_ptr juste pour ça. Utilisez simplement le constructeur explicite: table auto_ptr

(db-> query ("select * from t")); Ou déplacez le shared_ptr. – Eclipse

+1

Il y a une très bonne raison pour que le constructeur soit explicite. Il vous protège des erreurs accidentelles. C'est assez facile de mettre le auto_ptr (...) autour. – lothar

8

Vous devez utiliser

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

auto_ptr ne définit pas un opérateur d'affectation pour son type de modèle. La seule affectation autorisée provient d'un autre auto_ptr (et son constructeur est explicite). Ceci est fait pour protéger le mauvais usage accidentel de auto_ptr, car auto_ptr assume la propriété de la mémoire.

Je pense que vous avez besoin du formulaire de cession d'utiliser plusieurs requêtes après l'autre comme:

// 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

Pourquoi ne pas 'auto_ptr

Table (db-> query ("select * from t"));'? – avakar

+0

Eh bien, il a pensé que vous-même (voir sa question). Et le formulaire d'affectation est nécessaire chaque fois que vous faites plus d'une requête après l'autre :-) – lothar

+0

Je souhaite qu'il y avait un moyen de modifier les commentaires. uou - sacrément rapide dactylographie ;-) – lothar

2

Ajoutant à ce que lothar dit: Parce que le constructeur auto_ptr est déclarée avec le mot-clé explicit, vous besoin d'utiliser une distribution explicite pour créer un auto_ptr à partir d'un pointeur brut. (Avant l'introduction de explicit, le moulage implicite était le fléau de nombreux développeurs C++ nouveaux et expérimentés).

5

Le constructeur est déclaré comme explicite, ce qui signifie qu'il ne sera pas utilisé pour le cast de type implicite. La conversion implicite en auto_ptr pourrait facilement conduire à des situations indésirables puisque auto_ptr prend possession du pointeur. Par exemple, si auto_ptr autorisait la conversion implicite d'un pointeur et que vous passiez accidentellement un pointeur vers une méthode utilisant un auto_ptr, le pointeur serait converti silencieusement en un auto_ptr et serait ensuite supprimé lorsque la fonction se terminerait, même si cela ne t l'intention.Mais en marquant le constructeur comme une conversion explicite, vous ne pouvez plus passer en mode silencieux et en appelant le constructeur, vous exprimez clairement l'intention de transmettre la propriété à auto_ptr, évitant ainsi toute confusion potentielle.

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 
Questions connexes