77 votes

Comment puis-je utiliser des types de retour covariants avec des pointeurs intelligents ?

J'ai un code comme celui-ci :

class RetInterface {...}

class Ret1: public RetInterface {...}

class AInterface
{
  public:
     virtual boost::shared_ptr<RetInterface> get_r() const = 0;
     ...
};

class A1: public AInterface
{
  public:
     boost::shared_ptr<Ret1> get_r() const {...}
     ...
};

Ce code ne se compile pas.

Dans le studio visuel il soulève

C2555 : le type de retour d'une fonction virtuelle diffère et n'est pas covariant

Si je n'utilise pas boost::shared_ptr mais renvoient des pointeurs bruts, le code se compile (je comprends que cela est dû à types de retour covariants en C++). Je vois que le problème vient du fait que boost::shared_ptr de Ret1 n'est pas dérivé de boost::shared_ptr de RetInterface . Mais je veux revenir boost::shared_ptr de Ret1 pour une utilisation dans d'autres classes, sinon je dois caster la valeur retournée après le retour.

  1. Est-ce que je fais quelque chose de mal ?
  2. Sinon, pourquoi le langage est-il ainsi fait - il devrait être extensible pour gérer la conversion entre pointeurs intelligents dans ce scénario ? Existe-t-il une solution de contournement souhaitable ?

0 votes

Si vous n'utilisez pas boost::shared_ptr, retournez-vous des pointeurs ? Est-ce du C++ géré ?

0 votes

@Lev Si j'essaie de retourner des pointeurs bruts, le code se compile, mais il y a alors un problème de gestion de la mémoire. Non, je n'utilise pas de C++ géré.

0 votes

Ce que je fais : je renvoie des pointeurs bruts mais je précise que l'appelant est responsable de l'encapsulation du pointeur dans un pointeur intelligent, par ex. std::unique_ptr<Class>(obj.clone()) .

30voto

Anthony Williams Points 28904

Tout d'abord, c'est bien ainsi que cela fonctionne en C++ : le type de retour d'une fonction virtuelle dans une classe dérivée doit être le même que dans la classe de base. Il y a l'exception spéciale qu'une fonction qui renvoie une référence/pointeur à une classe X peut être surchargée par une fonction qui renvoie une référence/pointeur à une classe qui dérive de X, mais comme vous le notez, cela ne permet pas d'utiliser le type de retour de la fonction virtuelle. intelligent les pointeurs (tels que shared_ptr ), juste pour les pointeurs ordinaires.

Si votre interface RetInterface est suffisamment complète, alors vous n'aurez pas besoin de connaître le type réel retourné dans le code appelant. En général, cela n'a pas de sens de toute façon : la raison pour laquelle get_r est un virtual en premier lieu parce que vous l'appellerez par le biais d'un pointeur ou d'une référence à la classe de base. AInterface Dans ce cas, vous ne pouvez pas savoir quel type la classe dérivée retournerait. Si vous l'appelez avec une classe A1 vous pouvez simplement créer une référence séparée get_r1 fonction dans A1 qui fait ce dont vous avez besoin.

class A1: public AInterface
{
  public:
     boost::shared_ptr<RetInterface> get_r() const
     {
         return get_r1();
     }
     boost::shared_ptr<Ret1> get_r1() const {...}
     ...
};

Alternativement, vous pouvez utiliser le modèle visiteur ou quelque chose comme ma Double envoi dynamique pour passer un callback à l'objet retourné qui peut alors invoquer le callback avec le bon type.

0 votes

Est-ce que boost::shared_ptr<Ret1> sera automatiquement converti en boost::shared_ptr<RetInterface> ? Ne faut-il pas au moins quelque chose comme boost::static_pointer_cast ?

7 votes

Renvoyer le pointeur vers la classe dérivée a du sens avec les méthodes de clonage sur les classes implémentant plusieurs interfaces.

8voto

user252836 Points 83

Cette question a été posée il y a longtemps, mais personne n'a donné une très bonne réponse. Voir http://lists.boost.org/boost-users/2007/11/31932.php pour savoir comment en fait obtenir ce que vous voulez.

2voto

Mr Fooz Points 21092

Vous ne pouvez pas modifier les types de retour (pour les types de retour non pointeur, non référence) lorsque vous surchargez des méthodes en C++. A1::get_r doit retourner un boost::shared_ptr<RetInterface> .

Anthony Williams a une belle vue d'ensemble réponse .

1voto

morabot Points 1

Que pensez-vous de cette solution ?

template<typename Derived, typename Base>
class SharedCovariant : public shared_ptr<Base>
{
public:

typedef Base BaseOf;

SharedCovariant(shared_ptr<Base> & container) :
    shared_ptr<Base>(container)
{
}

shared_ptr<Derived> operator ->()
{
    return boost::dynamic_pointer_cast<Derived>(*this);
}
};

par exemple

struct A {};

struct B : A {};

struct Test
{
    shared_ptr<A> get() {return a_; }

    shared_ptr<A> a_;
};

typedef SharedCovariant<B,A> SharedBFromA;

struct TestDerived : Test
{
    SharedBFromA get() { return a_; }
};

-1voto

Mark Ransom Points 132545

M. Fooz a répondu à la première partie de votre question. Partie 2, cela fonctionne de cette façon parce que le compilateur ne sait pas s'il appellera AInterface::get_r ou A1::get_r au moment de la compilation - il doit savoir quelle valeur de retour il va obtenir, donc il insiste pour que les deux méthodes retournent le même type. Cela fait partie de la spécification C++.

Pour la solution de contournement, si A1::get_r renvoie un pointeur vers RetInterface, les méthodes virtuelles de RetInterface fonctionneront toujours comme prévu, et l'objet approprié sera supprimé lorsque le pointeur sera détruit. Il n'y a pas besoin de types de retour différents.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X