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
s par référence pour éviter une copie inutile:
int foo(const shared_ptr<bar>& ptr);
mais cela ne fonctionne pas si j'essaye 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:
- A
dynamic_cast
semble un peu excessif pour une simple distribution dérivée à la 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é être un problème de fichier d'en-tête manquant. De plus, ce que j'essayais de faire ici est considéré comme un anti-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 toute la durée de la fonction) doivent prendre une simple référence ou un pointeur, par exemple
int foo(bar& b)
.Les fonctions qui consomment un objet (c'est-à-dire sont les utilisateurs finaux d'un objet donné) devraient prendre une
unique_ptr
valeur par, par exempleint foo(unique_ptr<bar> b)
. Les appelants doiventstd::move
la valeur dans la fonction.Les fonctions qui prolongent la durée de vie d'un objet doivent prendre une
shared_ptr
valeur par, par exempleint foo(shared_ptr<bar> b)
. Les conseils habituels pour éviter les références circulaires s'appliquent.
Voir la présentation de Herb Sutter Back to Basics pour plus de détails.
la source
shared_ptr
? Pourquoi pas de référence const de barre?dynamic
casting n'est nécessaire que pour le downcasting. En outre, passer le pointeur dérivé devrait fonctionner correctement. Il va créer un nouveaushared_ptr
avec le même refcount (et l'augmenter) et un pointeur vers la base, qui se lie ensuite à la référence const. Puisque vous prenez déjà une référence, cependant, je ne vois pas pourquoi vous voulez en prendre unshared_ptr
. Prenez unBase const&
et appelezfoo(*bar)
.foo(bar)
) ne fonctionne pas, du moins dans MSVC 2010.shared_ptr
à passer à la fonction? Je suis assez sûr qu'il n'y a aucun moyen d'éviter cela.Réponses:
Bien que
Base
etDerived
soient covariants et bruts, les pointeurs agissent en conséquenceshared_ptr<Base>
et neshared_ptr<Derived>
sont pas covariants. Ildynamic_pointer_cast
s'agit de la manière correcte et la plus simple de gérer ce problème.( Modifier:
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érifications à l'exécution. Voir les commentaires ci-dessous.)Cependant, si votre
foo()
fonction 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 aconst Base&
et de déréférencer leshared_ptr
lors de sa transmissionfoo()
.void foo(const Base& base); [...] shared_ptr<Derived> spDerived = getDerived(); foo(*spDerived);
En passant, comme les
shared_ptr
types ne peuvent pas être covariants, les règles des conversions implicites entre les types de retour covariants ne s'appliquent pas lors du retour des types deshared_ptr<T>
.la source
shared_ptr<Derived>
sont implicitement convertibles enshared_ptr<Base>
, donc le code devrait fonctionner sans manigances de casting.shared_ptr<Ty>
a un constructeur qui prend ashared_ptr<Other>
et effectue la conversion appropriée siTy*
est implicitement convertible enOther*
. Et si un casting est nécessaire, celui-cistatic_pointer_cast
est approprié ici, nondynamic_pointer_cast
.shared_ptr
pour éviter le nombre de références, alors il n'y a vraiment aucune bonne raison d'utiliser ashared_ptr
en premier lieu. Il est préférable d'utiliser à laconst Base&
place.static_pointer_cast
, merci.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 { };
la source
class
est pour les paramètres de modèle;struct
sert à définir les classes. (C'est au plus 45% une blague.)On dirait que vous essayez trop fort.
shared_ptr
est bon marché à copier; c'est l'un de ses objectifs. Les faire circuler par référence ne fait pas grand-chose. Si vous ne voulez pas de partage, passez le pointeur brut.Cela dit, il y a deux façons de faire cela auxquelles je peux penser du haut de ma tête:
foo(shared_ptr<Base>(bar)); foo(static_pointer_cast<Base>(bar));
la source
shared_ptr
implémentation fournie par Microsoft.Vérifiez également que le
#include
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 jetéstd::shared<base>
. J'avais déclaré en avant les deux classes afin que je puisse contenir des pointeurs vers elles, mais parce que je n'avais pas#include
le compilateur ne pouvait pas voir qu'une classe était dérivée de l'autre.la source