web-dev-qa-db-fra.com

polymorphic_allocator: quand et pourquoi devrais-je l'utiliser?

Here est la documentation sur cppreference , ici est le brouillon de travail.

Je dois admettre que je n'ai pas compris quel est le véritable objectif de polymorphic_allocator et quand/pourquoi/comment je devrais l'utiliser.
Par exemple, le pmr::vector a la signature suivante:

namespace pmr {
    template <class T>
    using vector = std::vector<T, polymorphic_allocator<T>>;
}

Que fait le polymorphic_allocator offre? Que fait le std::pmr::vector offre aussi bien à l’égard de l’ancien std::vector? Que puis-je faire maintenant que je n'ai pas pu faire jusqu'à maintenant?
Quel est le but réel de cet allocateur et quand devrais-je l'utiliser réellement?

77
skypjack

Citation de choix de cppreference:

Ce polymorphisme au moment de l'exécution permet aux objets utilisant polymorphic_allocator de se comporter comme s'ils utilisaient différents types d'allocateurs au moment de l'exécution, malgré le même type d'allocateur statique

Le problème avec les allocateurs "normaux" est qu'ils changent le type du conteneur. Si vous voulez un vector avec un allocateur spécifique, vous pouvez utiliser le paramètre Allocator template:

auto my_vector = std::vector<int,my_allocator>();

Le problème est maintenant que ce vecteur n'est pas du même type qu'un vecteur avec un allocateur différent. Vous ne pouvez pas le transmettre à une fonction nécessitant un vecteur d'allocation par défaut, par exemple, ni affecter deux vecteurs avec un type d'allocateur différent à la même variable/pointeur, par exemple:

auto my_vector = std::vector<int,my_allocator>();
auto my_vector2 = std::vector<int,other_allocator>();
auto vec = my_vector; // ok
vec = my_vector2; // error

Un allocateur polymorphe est un type d'allocateur unique avec un membre qui peut définir le comportement de l'allocateur via une répartition dynamique plutôt que via le mécanisme de modèle. Cela vous permet d'avoir des conteneurs qui utilisent une allocation spécifique et personnalisée, mais qui sont toujours d'un type commun.

La personnalisation du comportement de l’allocateur se fait en donnant à l’allocateur un std::memory_resource *:

// define allocation behaviour via a custom "memory_resource"
class my_memory_resource : public std::pmr::memory_resource { ... };
my_memory_resource mem_res;
auto my_vector = std::pmr::vector<int>(0, &mem_res);

// define a second memory resource
class other_memory_resource : public std::pmr::memory_resource { ... };
other_memory_resource mem_res_other;
auto my_other_vector = std::pmr::vector<int>(0, &mes_res_other);

auto vec = my_vector; // type is std::pmr::vector<int>
vec = my_other_vector; // this is ok -
      // my_vector and my_other_vector have same type

La principale question qui reste, à mon avis, est qu'un std::pmr:: Le conteneur n’est toujours pas compatible avec l’équivalent std:: conteneur en utilisant l'allocateur par défaut. Vous devez prendre des décisions au moment de concevoir une interface qui fonctionne avec un conteneur:

  • est-il probable que le conteneur transmis puisse nécessiter une allocation personnalisée?
  • si oui, dois-je ajouter un paramètre de modèle (pour permettre des allocateurs arbitraires) ou devrais-je imposer l'utilisation d'un allocateur polymorphe?

Un modèle de solution autorise toute allocateur, y compris un allocateur polymorphe, mais présente d’autres inconvénients (taille du code généré, date de compilation, le code doit être exposé dans un fichier d’en-tête, possibilité de "contamination de type" qui continue de pousser le problème extérieur). Une solution d'allocateur polymorphe dictera en revanche qu'un allocateur polymorphe doit être utilisé. Ceci empêche d'utiliser std:: conteneurs qui utilisent l’allocateur par défaut et peuvent avoir des implications pour l’interfaçage avec du code hérité.

Comparé à un allocateur classique, un allocateur polymorphe a certains coûts mineurs, tels que la surcharge de stockage du pointeur memory_resource (qui est probablement négligeable) et le coût de la répartition de la fonction virtuelle pour les allocations. Le principal problème, en réalité, est probablement le manque de compatibilité avec le code existant qui n’utilise pas d’allocateurs polymorphes.

66
davmac

polymorphic_allocator est destiné à un allocateur personnalisé comme std::function est un appel de fonction direct.

Il vous permet simplement d'utiliser un allocateur avec votre conteneur sans avoir à décider, au moment de la déclaration, lequel. Donc, si vous avez une situation où plus d’un allocateur conviendrait, vous pouvez utiliser polymorphic_allocator.

Peut-être souhaitez-vous masquer le programme d'allocation utilisé pour simplifier votre interface ou peut-être voulez-vous l'échanger pour différents cas d'exécution.

Tout d'abord, vous avez besoin d'un code nécessitant un allocateur, puis vous devez vouloir pouvoir échanger celui qui est utilisé, avant de considérer le vecteur pmr.

23

Un inconvénient des allocateurs polymorphes est que polymorphic_allocator<T>::pointer est toujours juste T*. Cela signifie que vous ne pouvez pas les utiliser avec pointeurs fantaisie . Si vous voulez faire quelque chose comme placer les éléments d’un vector dans la mémoire partagée et y accéder par le biais de boost::interprocess::offset_ptrs , vous devez utiliser un allocateur classique non-polymorphe ancien pour cela.

Ainsi, bien que les allocateurs polymorphes vous permettent de modifier le comportement d'allocation sans changer le type statique d'un conteneur, ils limitent ce qu'est une allocation .

2
Maxpm