web-dev-qa-db-fra.com

Comment unique_ptr peut-il ne pas avoir de temps système s'il doit stocker le fichier supprimé?

Jetons d'abord un coup d'œil à ce que C++ Primer disait à propos de unique_ptr et shared_ptr:
16.1.6 $. Efficacité et flexibilité

Nous pouvons être certains que shared_ptr ne considère pas le suppresseur en tant que membre direct,} _, car le type de ce suppresseur n’est connu qu’au moment de l’exécution.

Le type du membre deleter faisant partie du type d'un unique_ptr, il est connu au moment de la compilation. Le deleter peut être stocké directement dans chaque objet unique_ptr. 

Il semble donc que le shared_ptr ne possède pas de membre direct de deleter, mais que unique_ptr l’ait. Cependant, la réponse la plus votée d'une autre question dit:

Si vous fournissez le paramètre deleter en tant qu'argument de modèle (comme dans unique_ptr), il fait partie du type et vous vous n'avez pas besoin de stocker quoi que ce soit dans les objets de ce type. Si deleter est passé en tant qu'argument du constructeur (comme dans shared_ptr) vous devez le stocker dans l'objet. Cela représente un coût supplémentaire en termes de flexibilité, car vous pouvez utiliser différents suppresseurs pour les objets du même type. 

Les deux paragraphes cités sont totalement contradictoires, ce qui me rend confus. De plus, beaucoup de gens disent que unique_ptr est égal à zéro surcharge car il n’a pas besoin de stocker le suppresseur en tant que membre. Cependant, comme nous le savons, unique_ptr a un constructeur de unique_ptr<obj,del> p(new obj,fcn), ce qui signifie que nous pouvons lui passer un suppresseur, donc unique_ptr semble avoir stocké deleter en tant que membre. Quel bordel!

25
bigxiao

std::unique_ptr<T> risque fort d'être nul (avec n'importe quelle implémentation rationnelle de bibliothèque standard). std::unique_ptr<T, D>, pour une D arbitraire, n'est généralement pas à zéro.

La raison est simple: l'optimisation de base vide peut être utilisée pour éliminer le stockage du suppresseur s'il s'agit d'un type vide (et donc sans état) (tel que les instanciations std::default_delete).

31
Angew

La phrase clé qui semble vous confondre est "Le suppresseur peut être enregistré directement". Mais rien ne sert de stocker un deleter de type std::default_delete. Si vous en avez besoin, vous pouvez simplement en créer un en tant que std::default_delete{}

En général, il n'est pas nécessaire de stocker les suppresseurs sans état, car vous pouvez les créer sur demande.

12
MSalters

La réponse d'Angew expliqué assez en détail ce qui se passe.

Pour les curieux de voir comment les choses pourraient se voir sous les couvertures

template<typename T, typename D, bool Empty = std::is_empty_v<D>>
class unique_ptr
{
    T* ptr;
    D d;

    // ... 
};

template<typename T, typename D>
class unique_ptr<T, D, true> : D
{
    T* ptr;

    // ...
};

Qui se spécialise dans les deleters vides et tire parti de l'optimisation de la base vide .

10
Passer By

Brève introduction:

unique_ptr can introduit une petite surcharge, mais pas à cause du deleter, mais parce que lorsque vous vous en déplacez, la valeur doit être null où il pointe toujours là où il a indiqué avant. De toute évidence, l'optimiseur intelligent peut optimiser, mais ce n'est pas garanti.

Retour au deleter:

Les autres réponses sont correctes, mais complexes. Voici donc la version simplifiée sans mention d’OBA ou d’autres termes complexes.

Si deleter est vide (n'a pas d'état), vous n'avez pas besoin de le conserver à l'intérieur de unique_ptr. Si vous en avez besoin, vous pouvez simplement le construire quand vous en avez besoin. Tout ce que vous devez savoir, c'est le type deleter (et c'est l'un des arguments de modèle pour unique_ptr).

Par exemple, considérons le code suivant, qui illustre également la création simple à la demande d’un objet sans état. 

#include <iostream>
#include <string>
#include <string_view>

template<typename Person>
struct Greeter{
    void greet(){
        static_assert(std::is_empty_v<Person>, "Person must be stateless");
        Person p; // Stateless Person instance constructed on demand
        std::cout << "Hello " << p() << std::endl;
    }
    // ... and not kept as a member.
};

struct Bjarne{
    std::string_view operator()(){
        return "Bjarne";
    }
};

int main() {
    Greeter<Bjarne> hello_bjarne;
    hello_bjarne.greet();
}
0
NoSenseEtAl