Question

Mise à jour:Édité exemple de code pour utiliser AutoA pour la solution de contournement (qui a l'intention d'origine).Réalisé cela après avoir vu rlbond de réponse.

Je suis en train d'intégrer l'utilisation de auto_ptr dans mon code sur la base des recommandations de ce fil:

Exprimer l'utilisation de C++ arguments par le biais de la méthode des interfaces

Cependant, je reçois des imprévus de la compilation des erreurs lors de la compilation avec Visual Studio 6.0.Il a un problème en ce qui concerne les affectations/copies d'un std::auto_ptr d'un type dérivé d'un std::auto_ptr de type de base.Est-ce un problème spécifique à mon compilateur?

Je sais qu'il y a une forte recommandation de l'utilisation de Boost, mais sur mon projet, il n'est pas une option.Si je veux continuer à les utiliser auto_ptr, suis-je obligé d'utiliser la solution de contournement de l'appel std::auto_ptr::release()?De ce que j'ai rencontrés jusqu'à présent, cette question génère une erreur de compilation, il est donc assez facile à attraper.Toutefois, on pourrait adopter la convention de l'appel à la libération d'attribuer à un "auto_ptr" de type de base tout au long exposé à des problèmes d'entretien?Surtout si elle est construite avec un autre compilateur (en supposant que d'autres compilateurs n'ont pas ce problème).

Si l' release() solution de contournement n'est pas bonne à cause de ma situation, je devrais tomber en arrière sur l'utilisation d'une convention différente pour décrire le transfert de propriété?

Voici un exemple illustrant le problème.

#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;
}

Erreur De Compilation:

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)
Était-ce utile?

La solution

Le premier est facile:

AutoA b_auto_to_a_auto(b_auto);  //fails to compile

Cette échoue sur VC6, car il nécessite membre modèles de fonction, quelque chose de VC6 de la bibliothèque standard ne prend pas en charge.Il compile sur conforme à la norme, des compilateurs, des si.

Solution de contournement:

AutoA b_auto_to_a_auto( b_auto.release() );

Le second est beaucoup plus subtil :)

sink(b_raw_to_b_auto);  //fails to compile

Ce que l'on ne devrait pas compiler sur une conforme aux normes compilateur, parce qu'il y a une conversion implicite en cours.Le compilateur transforme le ci-dessus dans

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

cependant, sink prend std::auto_ptr<A> par valeur, de sorte que le temporaire std::auto_ptr<A> créé implicitement par le compilateur doit être copier-construit dans l'argument de sink.Maintenant, temporaires comme ça rvalues.Rvalues ne pas se lier à des non-const références, mais std::auto_ptr's "constructeur de copie" prend son argument par non-const de référence.

Là vous allez - erreur de compilation.AFAICS c'est les normes d'un tel comportement.C++0x "sémantique de déplacement" sera corrigé que par l'ajout d'un "constructeur de copie" qui prend une référence rvalue, si je ne suis pas sûr de savoir comment beaucoup d'amour std::auto_ptr va encore recevoir dans l'avenir, ce qui avec std::shared_ptr et tous les.

Solution de contournement pour la seconde:

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

Autres conseils

AutoA b_auto_to_a_auto(b_auto);  //fails to compile

sink(b_raw_to_b_auto);  //fails to compile

Pavel Minaev signale quelque chose que je ne savais pas en fait:

Le premier appel doit compiler, parce qu'il ya une conversion implicite d'un B * A * à un. Cependant, le second ne sera pas compilé. La volonté suivante:

sink(static_cast<AutoA>(b_raw_to_b_auto));

VC6 est connu pour ne pas être très bon avec les modèles.

Je vous suggère fortement de mettre à jour votre base de code qui fonctionne réellement et commencer à utiliser des techniques de RAII, en particulier boost::shared_ptr. Je sais que vous dites que vous ne pouvez pas, et je sais qu'il est difficile, mais vous aurez pratiquement pas de fuites de mémoire et beaucoup, beaucoup moins de bugs.

Et puis, peut-être même sans les fonctionnalités que vous pouvez utiliser auto_ptr?

Il y a deux questions ici. Tout d'abord, ceci:

AutoA b_auto_to_a_auto(b_auto);  

Il est parfaitement conforme à la norme et devrait compiler. Permettez-moi de vous expliquer pourquoi. ISO C ++ norme spécifie (20.4.5.1 [lib.auto.ptr.cons] / 4-6) pour le constructeur suivant auto_ptr<X> (entre autres);

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

Notez que Y est un type différent de X ici. Et la norme dit en outre:

  

Nécessite: Y * peut être converti implicitement X *

.

La seule chose à faire attention ici est que l'argument du constructeur est une référence à la non-const. Pour votre cas, ce n'est pas un problème (puisque vous y passer une variable non-const), mais il devient important pour la partie suivante. Pour conclure ici: ce que vous voyez est comportement non standard dans VC6. Il devrait compiler sur un compilateur compatible (et compiler sur VC7 et au-dessus). Passons maintenant à la deuxième chose:

sink(b_raw_to_b_auto);  //fails to compile

Celui-ci est en fait parfaitement expliqué par mmutz, donc je ne vais pas entrer dans les détails - voir sa réponse. Pour conclure sur ce point:. Oui, cette ligne ne devrait pas compiler, et ne sera pas dans un compilateur compatible (ou VC6, comme vous avez découvert)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top