J'ai une classe avec un membre unique_ptr.
class Foo {
private:
std::unique_ptr<Bar> bar;
...
};
La barre est une classe tierce qui a une fonction create () et une fonction destroy ().
Si je voulais utiliser un std::unique_ptr
avec lui dans une fonction autonome, je pourrais faire:
void foo() {
std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); });
...
}
Existe-t-il un moyen de faire cela en std::unique_ptr
tant que membre d'une classe?
c++
c++11
move-semantics
unique-ptr
huitlarc
la source
la source
std::unique_ptr<Bar, decltype(&destroy)> ptr_;
unique_ptr
(ils doivent tous stocker le pointeur de fonction avec le pointeur vers les données réelles), nécessite de passer la fonction de destruction à chaque fois, elle ne peut pas être intégrée (car le modèle ne peut pas se spécialiser dans la fonction spécifique, uniquement la signature), et doit appeler la fonction via le pointeur (plus coûteux que l'appel direct). Les réponses de rici et de Deduplicator évitent tous ces coûts en se spécialisant dans un foncteur.Il est possible de le faire proprement en utilisant un lambda en C ++ 11 (testé dans G ++ 4.8.2).
Compte tenu de ce réutilisable
typedef
:Tu peux écrire:
Par exemple, avec un
FILE*
:Avec cela, vous bénéficiez des avantages d'un nettoyage sans risque d'exception à l'aide de RAII, sans avoir besoin de bruit d'essai / capture.
la source
std::function
dans la définition ou autre?std::function
. La classe Lambda ou personnalisée comme dans la réponse acceptée peut être intégrée contrairement à cette solution. Mais cette approche présente un avantage dans le cas où vous souhaitez isoler toute implémentation dans un module dédié.deleted_unique_ptr<Foo> foo(new Foo(), customdeleter);
sicustomdeleter
suit la convention (il retourne void et accepte le pointeur brut comme argument).Il vous suffit de créer une classe de suppression:
et fournissez-le comme argument modèle de
unique_ptr
. Vous devrez toujours initialiser le unique_ptr dans vos constructeurs:Autant que je sache, toutes les bibliothèques c ++ populaires implémentent cela correctement; comme
BarDeleter
n'a en fait aucun état, il n'a pas besoin d'occuper d'espace dans le fichierunique_ptr
.la source
struct BarDeleter
) àstd::unique_ptr
(std::unique_ptr<Bar, BarDeleter>
) qui permet austd::unique_ptr
constructeur de créer lui-même une instance Deleter. c'est-à-dire que le code suivant est autoriséstd::unique_ptr<Bar, BarDeleter> bar[10];
typedef std::unique_ptr<Bar, BarDeleter> UniqueBarPtr
unique_ptr
, pas besoin de fournir une instance du suppresseur lors de la construction), et ajoute l'avantage de pouvoir utiliserstd::unique_ptr<Bar>
n'importe où sans avoir besoin de se souvenir pour utiliser letypedef
fournisseur spécial ou explicitement le deuxième paramètre de modèle. (Pour être clair, c'est une bonne solution, j'ai voté à la hausse, mais cela arrête une étape de moins qu'une solution transparente)À moins que vous n'ayez besoin de pouvoir modifier le suppresseur au moment de l'exécution, je vous recommande vivement d'utiliser un type de suppression personnalisé. Par exemple, si l'on utilise un pointeur de fonction pour votre Deleter,
sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*)
. En d'autres termes, la moitié des octets de l'unique_ptr
objet sont gaspillés.Cependant, écrire un suppresseur personnalisé pour envelopper chaque fonction est un problème. Heureusement, nous pouvons écrire un type basé sur la fonction:
Depuis C ++ 17:
Avant C ++ 17:
la source
deleter_from_fn
est.Vous savez, utiliser un suppresseur personnalisé n'est pas la meilleure façon de procéder, car vous devrez le mentionner partout dans votre code.
Au lieu de cela, comme vous êtes autorisé à ajouter des spécialisations aux classes au niveau de l'espace de noms
::std
tant que des types personnalisés sont impliqués et que vous respectez la sémantique, procédez comme suit :Spécialisez
std::default_delete
:Et peut-être aussi faire
std::make_unique()
:la source
std
ouvre une toute nouvelle boîte de vers. Notez également que la spécialisation destd::make_unique
n'est pas autorisée après C ++ 20 (donc ne devrait pas être faite avant) car C ++ 20 interdit la spécialisation de choses dansstd
lesquelles ne sont pas des modèles de classe (std::make_unique
est un modèle de fonction). Notez que vous vous retrouverez probablement également avec UB si le pointeur passé dansstd::unique_ptr<Bar>
n'a pas été alloué depuiscreate()
, mais depuis une autre fonction d'allocation.std::default_delete
répond à l'exigence du modèle d'origine. J'imagine que cestd::default_delete<Foo>()(p)
serait une manière valide d'écriredelete p;
, donc sidelete p;
serait valide pour écrire (c'est-à-dire siFoo
c'est complet), ce ne serait pas le même comportement. De plus, si l'delete p;
écriture n'était pas valide (Foo
est incomplète), ce serait spécifier un nouveau comportement pourstd::default_delete<Foo>
, plutôt que de garder le même comportement.make_unique
spécialisation est problématique, mais j'ai définitivement utilisé lastd::default_delete
surcharge (pas de modèle avecenable_if
, juste pour les structures C comme OpenSSLBIGNUM
qui utilisent une fonction de destruction connue, où le sous-classement ne se produira pas), et c'est de loin l'approche la plus simple, car le reste de votre code peut simplement être utiliséunique_ptr<special_type>
sans avoir à passer le type de foncteur comme modèleDeleter
partout, ni utilisertypedef
/using
pour donner un nom audit type pour éviter ce problème.Vous pouvez simplement utiliser
std::bind
avec une fonction de destruction.Mais bien sûr, vous pouvez également utiliser un lambda.
la source