2009-04-01 7 views
3

Dans une réponse à Is it safe to store objects of a class which has an std::auto_ptr as its member variable in std::vector?, j'ai déclaré qu'une classe contenant un auto_ptr pouvait être stockée dans un vecteur à condition que la classe comprenait un constructeur de copie défini par l'utilisateur.Classe contenant auto_ptr stockée dans le vecteur

Il y avait plusieurs commentaires suggérant que ce n'était pas le cas, donc cette question est une tentative pour résoudre le problème. Considérez le code suivant:

#include <memory> 
#include <vector> 
using namespace std; 

struct Z {}; 

struct A { 

    A(Z z) 
     : p(new Z(z)) {} 

    A(const A & a) 
     : p(a.p.get() ? new Z(*a.p.get()) : 0) {} 

    // no assigment op or dtor defined by intent 

    auto_ptr <Z> p; 
}; 

int main() { 
    vector <A> av;    
    Z z;      
    A a(z); 
    av.push_back(a);  
    av.push_back(A(z)); 
    av.clear();    
}       

S'il vous plaît examiner la & ci-dessus dans votre réponse indiquer où le comportement non défini au sens de la norme C++ pourrait se produire pour cette classe particulière utilisée de cette manière particulière. Je ne suis pas intéressé si la classe est utile, sage, triable, ou comment il fonctionne sous des exceptions.

S'il vous plaît noter également que ce n'est pas une question sur la validité de la création d'un vecteur de auto_ptrs - Je suis bien conscient des problèmes à ce sujet.

Merci à tous pour vos entrées sur ce que dans rétrospectivement est probablement une question stupide. plutôt Je suppose que je me suis concentré trop sur la copie ctor & oublié affectation. Le gagnant de mes points d'acceptation (et les points signifient prix!) Est litb pour une généralement explication exhaustive (désolé Earwicker)

+0

Est-ce une question du 1er avril? AFAIK, vous êtes plus capable, que la plupart d'entre nous de comprendre UB :-) – dirkgently

+0

Je savais que quelqu'un demanderait ceci :-) Mais non, je suis sérieux. –

Répondre

3

Essayer de mettre la liste de lieux ensemble qui rend l'exemple comportement non défini.

#include <memory> 
#include <vector> 
using namespace std; 

struct Z {}; 

struct A { 

    A(Z z) 
     : p(new Z(z)) {} 

    A(const A & a) 
     : p(a.p.get() ? new Z(*a.p.get()) : 0) {} 

    // no assigment op or dtor defined by intent 

    auto_ptr <Z> p; 
}; 

int main() { 
    vector <A> av; 
    ... 
} 

Je vais examiner les lignes jusqu'à celui où vous instancier le vecteur avec votre type A. La norme doit dire

En 23.1/3:

Le type d'objets stockés dans ces composants doivent satisfaire aux exigences des types CopyConstructible (20.1.3), et les exigences supplémentaires des types assignables.

Dans 23.1/4 (non souligné):

Dans le tableau 64, T est le type utilisé pour instancier le conteneur, t est une valeur de T, et u est une valeur de (éventuellement const) T.

+-----------+---------------+---------------------+ 
|expression |return type |postcondition  | 
+-----------+---------------+---------------------+ 
|t = u  |T&    |t is equivalent to u | 
+-----------+---------------+---------------------+ 

Tableau 64

Dans 12.8/10:

Si la définition de classe ne déclare pas explicitement un opérateur d'affectation de copie, un est déclaré implicitement.L'opérateur d'affectation de copie implicitement déclarée pour une classe X aura la forme

X& X::operator=(const X&) 

si

  • chaque classe de base directe B de X a un opérateur d'affectation de copie dont le paramètre est de type const B &, ou B, et
  • pour tous les membres de données non statiques de X de classe M (ou matrice), chaque type de classe a un opérateur d'affectation de copie dont le paramètre est de type const M & , c onst volatile M & ou M.

Sinon, l'opérateur d'affectation de copie déclarée implicitement auront la forme

X& X::operator=(X&) 

(Notez la dernière et dernière phrase)

En 17.4.3.6/1 and /2:

Dans certains cas (fonctions de remplacement, h Fonctions andler, opérations sur les types utilisés pour instancier les composants de modèle de bibliothèque standard), la bibliothèque C++ Standard dépend des composants fournis par un programme C++. Si ces composants ne répondent pas à leurs exigences, la norme n'impose aucune exigence relative à la mise en œuvre.

En particulier, les effets ne sont pas définis dans les cas suivants:

  • pour les types utilisés comme arguments de modèle lors de l'instanciation d'un composant de modèle, si les opérations sur le type ne mettent pas en œuvre la sémantique du paragraphe applicable Exigences (20.1.5, 23.1, 24.1, 26.1). Les opérations sur ces types peuvent signaler un échec en lançant une exception, sauf indication contraire.

Maintenant, si vous regardez la spécification de auto_ptr vous remarquerez qu'il a un opérateur copie affectation qui prend un non-const auto_ptr. Ainsi, l'opérateur d'affectation de copie implicitement déclaré de votre classe également prendra comme paramètre un type non-const. Si vous lisez attentivement les endroits ci-dessus, vous verrez comment il est dit que l'instanciation d'un vecteur avec votre type tel que écrit est un comportement indéfini.

+0

Mais ma classe a un constructeur de copie _explicitly_ déclaré, donc je ne vois pas comment cela s'applique. –

+0

cela ne s'applique pas du tout. c'est l'opérateur d'affectation de copie qui manque - pas le constructeur de copie. Je dirais que tel que défini, votre constructeur de copie est très bien. –

+0

oups - ma mauvaise lecture - désolé –

0

Depuis la auto_ptr régulière sémantique pourrait suggère que la propriété est passée au cours la copie, je préfère utiliser ici boost::scoped_ptr. Bien sûr, l'opérateur d'affectation est manquant.

3

Je ne pense pas nécessairement que le code ci-dessus va même compiler. Sûrement l'implémenteur de std::vector est libre d'exiger qu'un opérateur d'assignation soit disponible, de ?

Et juste après avoir essayé, il ne compile pas sur Visual Studio C++ 2008 Service Pack 1:

binaire '=': aucun opérateur trouvé qui prend un opérande à droite de type ' const A »(ou il n'y a pas de conversion acceptable)

Je pense que, sur la direction de Herb Sutter, les classes de conteneurs dans VC++ font tous les efforts pour imposer les exigences de la norme sur les paramètres de type, en particulier pour rendre difficile à utiliser auto_ptr avec eux. Ils ont peut-être outrepassé les limites fixées par la norme, bien sûr, mais il me semble me souvenir que cela exige une véritable attribution ainsi qu'une véritable construction de copies.

Cependant, il compile en g ++ 3.4.5.

+0

Oui, maintenant vous me rappelez que je me souviens de cela aussi - je suppose que cela répond à ma question :-( –

+0

Eh bien, où est ma tique verte pour être un garçon intelligent alors? :) –

+0

Toutes les bonnes choses viennent à celui qui attend. –

0

Qu'en est-il de ce qui suit?

cout << av[ 0 ] << endl; 

De même, conceptuellement, une copie doit laisser l'élément copié de manière inchangée. Ceci est violé dans votre implémentation.

(Il est tout à fait autre chose que votre code d'origine compile très bien avec g++ -pedantic ... et Comeau, mais pas VS2005.)

+0

"Aussi, conceptuellement, une copie devrait laisser l'élément copié à partir d'inchangé." - Essayez de dire ça à auto_ptr! –

+0

Ma question ne portait pas sur l'utilité de la classe - évidemment, il est complètement brisé, mais seulement sur UB. Mais comme Earwicker l'a souligné, je pense que VC++ peut être juste pour une fois.Intéressant à propos de Comeau ... –

+0

@Earwicker: C'est ce que je voulais dire à propos de auto_ptrs. – dirkgently

4

Les objets stockés dans des conteneurs sont tenus d'être « CopyConstructable », ainsi que « assignable » (C++ 2008 23.1/3). Votre classe essaye de gérer l'exigence CopyConstructable (bien que je prétende qu'elle ne la rencontre toujours pas - j'ai édité cet argument parce que ce n'est pas nécessaire et parce que c'est discutable je suppose), mais ça ne concerne pas avec l'exigence assignable. Pour être assignables (C++ 2008 23,1/4), ce qui suit doit être vrai où t est une valeur de T et u est une valeur de (peut-être const) T:

t = u retourne un T& et t est équivalent à u

La norme indique également dans une note (20.4.5/3): "auto_ptr ne répond pas aux exigences CopyConstructible et assignables pour les éléments de conteneurs bibliothèque standard et instanciation ainsi un conteneur bibliothèque standard avec un auto_ptr entraîne un comportement indéfini. "

Puisque vous ne déclarez pas ou définir un opérateur d'affectation, un un implicite sera fourni qui utilise l'opérateur d'affectation de auto_ptr, ce qui rend certainement t pas équivalent à u, sans compter que cela ne fonctionnera pas à tout pour les valeurs "const T u" (ce qui est ce que Earwicker's answer souligne - je ne fais que souligner la ou les portions exactes de la norme).

Questions connexes