27 votes

Comment peut-unique_ptr ont pas de frais généraux s'il a besoin de stocker la deleter?

D'abord jeter un oeil à ce que le C++ Primer dit à propos de unique_ptr et shared_ptr:
$16.1.6. L'efficacité et la Flexibilité

Nous pouvons être certains qu' shared_ptr ne tient pas la deleter en tant que membre direct, parce que le type de la deleter n'est pas connu jusqu'à l'exécution.

Parce que le type de la deleter fait partie du type de unique_ptr, le type de la deleter membre est connu au moment de la compilation. La deleter peuvent être stockés directement dans chaque unique_ptr objet.

Il semble donc que l' shared_ptr n'ont pas un membre direct de la deleter, mais unique_ptr n'. Toutefois, le haut-voté réponse à une autre question , dit:

Si vous fournissez le deleter comme argument de modèle (comme en unique_ptr) il fait partie de la type et que vous n'avez pas besoin de stocker des données supplémentaires dans les objets de ce type. Si deleter est passé en argument du constructeur (comme en shared_ptr) vous avez besoin de le stocker dans l'objet. Ce est le coût de plus de souplesse, puisque vous pouvez utiliser différents deleters pour les objets de même type.

Les deux cités au paragraphe sont totalement contradictoires, ce qui me rend confus. Qui plus est, beaucoup de gens, dit - unique_ptr est zéro frais généraux , car il n'a pas besoin de stocker la deleter comme 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 passer un deleter, de sorte à l' unique_ptr semble avoir stocké deleter en tant que membre. Quel gâchis!

32voto

Angew Points 53063

std::unique_ptr<T> est tout à fait susceptibles d'être nulle, les frais généraux (avec toute sane standard-mise en œuvre de bibliothèque). std::unique_ptr<T, D>, pour un arbitraire D, n'est pas, en général, sans frais supplémentaires.

La raison en est simple: Vide-de la Base de l'Optimisation peut être utilisé pour éliminer le stockage de la deleter dans le cas où c'est vide (et donc apatrides) type (comme std::default_delete instanciations).

12voto

MSalters Points 74024

La phrase clé qui semble que vous confondez est "La deleter peuvent être stockées directement". Mais il n'y a pas de point dans le stockage d'un deleter de type std::default_delete. Si vous en avez besoin, vous pouvez simplement créer un comme l' std::default_delete{}.

En général, les apatrides deleters n'ont pas besoin d'être stockées, comme vous pouvez le créer sur la demande.

11voto

Passer By Points 9171

Angew la réponse de explique très bien ce qu'il se passe.

Pour ceux curieux de voir comment les choses pourraient regarder 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 pour vide deleters et profiter de vide optimisation de la base.

1voto

NoSenseEtAl Points 2342

Brève présentation:

unique_ptr peut introduire de la petite surcharge, mais pas à cause de la deleter, mais parce que lorsque vous vous déplacez à partir de la valeur doit être définie sur null où si vous étiez à l'aide de matières pointeurs que vous pouvez laisser les vieux pointeur dans le bogue sujettes mais légitimes de l'état où il pointe toujours à l'endroit où elle a fait avant. Évidemment smart optimiseur peut optimiser, mais il n'est pas garanti.

Retour à la deleter:

D'autres réponses sont correctes, mais complexe. Voici donc la version simplifiée sans mention de EBO ou l'autre des termes compliqués.

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

Pour exaple envisager de code suivant, que démontre aussi simple de création à la demande d'un apatride en objet.

#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();
}

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