web-dev-qa-db-fra.com

Passage de shared_ptr <Derived> en tant que shared_ptr <Base>

Quelle est la meilleure méthode pour passer un shared_ptr D'un type dérivé à une fonction qui prend un shared_ptr D'un type de base?

Je passe généralement shared_ptr Par référence pour éviter une copie inutile:

int foo(const shared_ptr<bar>& ptr);

mais cela ne fonctionne pas si j'essaie de faire quelque chose comme

int foo(const shared_ptr<Base>& ptr);

...

shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);

Je pourrais utiliser

foo(dynamic_pointer_cast<Base, Derived>(bar));

mais cela semble sous-optimal pour deux raisons:

  • Un dynamic_cast Semble un peu excessif pour un simple casting dérivé-à-base.
  • Si je comprends bien, dynamic_pointer_cast Crée une copie (quoique temporaire) du pointeur à passer à la fonction.

Y a-t-il une meilleure solution?

Mise à jour pour la postérité:

Il s'est avéré qu'il s'agissait d'un problème de fichier d'en-tête manquant. De plus, ce que j'essayais de faire ici est considéré comme un contre-modèle. Généralement,

  • Les fonctions qui n'ont pas d'impact sur la durée de vie d'un objet (c'est-à-dire que l'objet reste valide pendant la durée de la fonction) doivent prendre une référence ou un pointeur simple, par ex. int foo(bar& b).

  • Les fonctions qui consomment un objet (c'est-à-dire sont les utilisateurs finaux d'un objet donné) devraient prendre un unique_ptr En valeur, par ex. int foo(unique_ptr<bar> b). Les appelants doivent std::move La valeur dans la fonction.

  • Les fonctions qui prolongent la durée de vie d'un objet doivent prendre un shared_ptr Par valeur, par ex. int foo(shared_ptr<bar> b). Le conseil habituel pour éviter références circulaires s'applique.

Voir Herb Sutter's Back to Basics talk pour plus de détails.

70
Matt Kline

Bien que Base et Derived soient covariants et que les pointeurs bruts vers eux agissent en conséquence, shared_ptr<Base> Et shared_ptr<Derived> Sont pas covariants. Le dynamic_pointer_cast Est la manière correcte et la plus simple de gérer ce problème.

(Edit:static_pointer_cast Serait plus approprié car vous effectuez un cast de dérivé vers la base, ce qui est sûr et ne nécessite pas de vérification d'exécution. Voir les commentaires ci-dessous.)

Cependant, si votre fonction foo() ne souhaite pas participer à l'extension de la durée de vie (ou, plutôt, participer à la propriété partagée de l'objet), il est préférable d'accepter un const Base& et déréférencer le shared_ptr en le passant à foo().

void foo(const Base& base);
[...]
shared_ptr<Derived> spDerived = getDerived();
foo(*spDerived);

Soit dit en passant, étant donné que les types shared_ptr Ne peuvent pas être covariants, les règles de conversions implicites entre les types de retour covariants ne s'appliquent pas lors du renvoi de types de shared_ptr<T>.

39
Bret Kuhns

On dirait que vous essayez trop fort. shared_ptr est bon marché à copier; c'est l'un de ses objectifs. Les transmettre par référence ne fait pas grand-chose. Si vous ne voulez pas partager, passez le pointeur brut.

Cela dit, il y a deux façons de le faire auxquelles je peux penser du haut de ma tête:

foo(shared_ptr<Base>(bar));
foo(static_pointer_cast<Base>(bar));
11
Pete Becker

Cela se produira également si vous avez oublié de spécifier l'héritage public sur la classe dérivée, c'est-à-dire si comme moi vous écrivez ceci:

class Derived : Base
{
};
7
dshepherd

Vérifiez également que le #include du fichier d'en-tête contenant la déclaration complète de la classe dérivée se trouve dans votre fichier source.

J'ai eu ce problème. Le std::shared<derived> ne serait pas converti en std::shared<base>. J'avais déclaré en avant les deux classes afin de pouvoir leur tenir des pointeurs, mais parce que je n'avais pas le #include le compilateur n'a pas pu voir qu'une classe était dérivée de l'autre.

6
Phil Rosenberg