Namespaces
Variants

C++ named requirements: Allocator

From cppreference.net
C++ named requirements

Encapsule les stratégies pour l'accès/adressage, l'allocation/désallocation et la construction/destruction d'objets.

Chaque composant de la bibliothèque standard susceptible d'allouer ou de libérer de la mémoire, depuis std::string , std::vector , et tous les conteneurs , à l'exception de std::array (depuis C++11) et de std::inplace_vector (depuis C++26) , jusqu'à std::shared_ptr et std::function (jusqu'à C++17) , le fait via un Allocator : un objet de type classe qui satisfait aux exigences suivantes.

L'implémentation de nombreuses exigences d'allocateur est optionnelle car tous les AllocatorAwareContainer accèdent aux allocateurs indirectement via std::allocator_traits , et std::allocator_traits fournit l'implémentation par défaut de ces exigences.

Table des matières

Exigences

Étant donné

  • T , un type non const et non référence (jusqu'en C++11) type d'objet non const (depuis C++11) (jusqu'en C++17) type d'objet non qualifié cv (depuis C++17) ,
  • A , un type Allocator pour le type T ,
  • a , un objet de type A ,
  • B , le type Allocator correspondant pour un type d'objet non qualifié cv U (obtenu par rebind de A ),
  • b , un objet de type B ,
  • p , une valeur de type std:: allocator_traits < A > :: pointer , obtenue en appelant std:: allocator_traits < A > :: allocate ( ) ,
  • cp , une valeur de type std:: allocator_traits < A > :: const_pointer , obtenue par conversion de p ,
  • vp , une valeur de type std:: allocator_traits < A > :: void_pointer , obtenue par conversion de p ,
  • cvp , une valeur de type std:: allocator_traits < A > :: const_void_pointer , obtenue par conversion de cp ou de vp ,
  • xp , un pointeur déréférençable vers un type d'objet non qualifié cv X ,
  • r , une lvalue de type T obtenue par l'expression * p ,
  • n , une valeur de type std:: allocator_traits < A > :: size_type .
Types internes
Identifiant de type Type aliasé Exigences
A::pointer (optionnel) (non spécifié) [1]
A::const_pointer (optionnel) (non spécifié)
A::void_pointer (optionnel) (non spécifié)
  • Satisfait NullablePointer .
  • A::pointer est convertible en A::void_pointer .
  • B::void_pointer et A::void_pointer sont le même type.
A::const_void_pointer (optionnel) (non spécifié)
  • Satisfait NullablePointer .
  • A::pointer , A::const_pointer , et A::void_pointer sont convertibles en A::const_void_pointer .
  • B::const_void_pointer et A::const_void_pointer sont le même type.
A::value_type T
A::size_type (optionnel) (non spécifié)
  • Un type entier non signé.
  • Peut représenter la taille du plus grand objet que A peut allouer.
A::difference_type (optionnel) (non spécifié)
  • Un type entier signé.
  • Peut représenter la différence de deux pointeurs quelconques vers les objets alloués par A .
A::template rebind<U>::other
(optionnel) [2]
B
  • Pour tout U , B::template rebind<T>::other est A .
Opérations sur les pointeurs
Expression Type de retour Exigences
* p T&
* cp const T & * cp et * p identifient le même objet.
p - > m (tel quel) Identique à ( * p ) . m , si ( * p ) . m est bien défini.
cp - > m (tel quel) Identique à ( * cp ) . m , si ( * cp ) . m est bien défini.
static_cast < A :: pointer > ( vp ) (tel quel) static_cast < A :: pointer > ( vp ) == p
static_cast < A :: const_pointer > ( cvp ) (tel quel) static_cast < A :: const_pointer > ( cvp ) == cp
std:: pointer_traits < A :: pointer > :: pointer_to ( r ) (tel quel)
Opérations de stockage et de durée de vie
Expression Type de retour Exigences
a. allocate ( n ) A::pointer Alloue un stockage adapté à un tableau de type T[n] et crée le tableau, mais ne construit pas les éléments du tableau. Peut lever des exceptions. Si n == 0 , la valeur de retour n'est pas spécifiée.
a. allocate ( n, cvp ) (optionnel) Identique à a. allocate ( n ) , mais peut utiliser cvp ( nullptr ou un pointeur obtenu à partir de a. allocate ( ) ) de manière non spécifiée pour améliorer la localité.
a. allocate_at_least ( n ) (optionnel) (depuis C++23) std:: allocation_result

< A :: pointer >

Alloue un stockage adapté à un tableau de type T[cnt] et crée le tableau, mais ne construit pas les éléments du tableau, puis retourne { p, cnt } , où p pointe vers le stockage et cnt n'est pas inférieur à n . Peut lever des exceptions.
a. deallocate ( p, n ) (non utilisé) Désalloue le stockage pointé par p , qui doit être une valeur retournée par un appel précédent à allocate ou allocate_at_least (depuis C++23) qui n'a pas été invalidé par un appel intermédiaire à deallocate . n doit correspondre à la valeur précédemment passée à allocate ou être comprise entre la demande et le nombre d'éléments retournés via allocate_at_least (peut être égal à l'une ou l'autre borne) (depuis C++23) . Ne lève pas d'exceptions.
a. max_size ( ) (optionnel) A::size_type La plus grande valeur pouvant être passée à A :: allocate ( ) .
a. construct ( xp, args... ) (optionnel) (non utilisé) Construit un objet de type X dans un stockage préalablement alloué à l'adresse pointée par xp , en utilisant args... comme arguments du constructeur.
a. destroy ( xp ) (optionnel) (non utilisé) Détruit un objet de type X pointé par xp , mais ne désalloue aucun stockage.
Relation entre les instances
Expression Type de retour Exigences
a1 == a2 bool
  • true uniquement si la mémoire allouée par l'allocateur a1 peut être désallouée via a2 .
  • Établit une relation réflexive, symétrique et transitive.
  • Ne lève pas d'exceptions.
a1 ! = a2
  • Identique à ! ( a1 == a2 ) .
Déclaration Effet Exigences
A a1 ( a ) Construit par copie a1 de sorte que a1 == a .
(Note : Chaque Allocator satisfait également CopyConstructible .)
  • Ne lève pas d'exceptions.
A a1 = a
A a ( b ) Construit a de sorte que B ( a ) == b et A ( b ) == a .
(Note : Cela implique que tous les allocateurs liés par rebind maintiennent les ressources mutuelles, telles que les pools mémoire.)
  • Ne lève pas d'exceptions.
A a1 ( std :: move ( a ) ) Construit a1 de sorte qu'il soit égal à la valeur précédente de a .
  • Ne lève pas d'exceptions.
  • La valeur de a reste inchangée et a1 == a .
A a1 = std :: move ( a )
A a ( std :: move ( b ) ) Construit a de sorte qu'il soit égal à la valeur précédente de A ( b ) .
  • Ne lève pas d'exceptions.
Type-id Type alias Exigences
A::is_always_equal
(optionnel)
std::true_type ou std::false_type ou dérivé de ceux-ci.
Influence sur les opérations de conteneur
Expression Type de retour Description
a. select_on_container_copy_construction ( )
(optionnel)
A
  • Fournit une instance de A à utiliser par le conteneur qui est copié-construit à partir de celui qui utilise a actuellement.
  • (Retourne généralement soit une copie de a soit un A construit par défaut.)
Type-id Type alias Description
A::propagate_on_container_copy_assignment
(optionnel)
std::true_type ou std::false_type ou dérivé de ceux-ci.
  • std::true_type ou dérivé de celui-ci si l'allocateur de type A doit être copié lorsque le conteneur qui l'utilise est copié-assigné.
  • Si ce membre est std::true_type ou dérivé de celui-ci, alors A doit satisfaire CopyAssignable et l'opération de copie ne doit pas lever d'exceptions.
  • Notez que si les allocateurs des conteneurs source et cible ne sont pas égaux, l'assignation par copie doit désallouer la mémoire de la cible en utilisant l'ancien allocateur puis l'allouer en utilisant le nouvel allocateur avant de copier les éléments (et l'allocateur).
A::propagate_on_container_move_assignment
(optionnel)
  • std::true_type ou dérivé de celui-ci si l'allocateur de type A doit être déplacé lorsque le conteneur qui l'utilise est déplacé-assigné.
  • Si ce membre est std::true_type ou dérivé de celui-ci, alors A doit satisfaire MoveAssignable et l'opération de déplacement ne doit pas lever d'exceptions.
  • Si ce membre n'est pas fourni ou est dérivé de std::false_type et que les allocateurs des conteneurs source et cible ne sont pas égaux, l'assignation par déplacement ne peut pas prendre possession de la mémoire source et doit assigner-par-déplacement ou construire-par-déplacement les éléments individuellement, en redimensionnant sa propre mémoire si nécessaire.
A::propagate_on_container_swap
(optionnel)
  • std::true_type ou dérivé de celui-ci si les allocateurs de type A doivent être échangés lorsque deux conteneurs qui les utilisent sont échangés.
  • Si ce membre est std::true_type ou dérivé de celui-ci, le type A doit satisfaire Swappable et l'opération d'échange ne doit pas lever d'exceptions.
  • Si ce membre n'est pas fourni ou est dérivé de std::false_type et que les allocateurs des deux conteneurs ne sont pas égaux, le comportement de l'échange de conteneurs est indéfini.

Notes :

  1. Voir également fancy pointers ci-dessous.
  2. rebind est uniquement optionnel (fourni par std::allocator_traits ) si cet allocateur est un modèle de la forme SomeAllocator<T, Args> , où Args représente zéro ou plusieurs paramètres de type template supplémentaires.

Étant donné

  • x1 et x2 , objets de types (éventuellement différents) X::void_pointer , X::const_void_pointer , X::pointer , ou X::const_pointer
Alors, x1 et x2 sont des valeurs de pointeur équivalentes si et seulement si x1 et x2 peuvent être explicitement converties en deux objets correspondants px1 et px2 de type X::const_pointer , en utilisant une séquence de static_cast s utilisant uniquement ces quatre types, et l'expression px1 == px2 s'évalue à true .

Étant donné

  • w1 et w2 , objets de type X::void_pointer
Ensuite, pour l'expression w1 == w2 et w1 ! = w2 l'un ou les deux objets peuvent être remplacés par un objet de valeur équivalente de type X::const_void_pointer sans modification de la sémantique.

Étant donné

  • p1 et p2 , objets de type X::pointer
Ensuite, pour les expressions p1 == p2 , p1 ! = p2 , p1 < p2 , p1 <= p2 , p1 >= p2 , p1 > p2 , p1 - p2 , un ou les deux objets peuvent être remplacés par un objet de valeur équivalente de type X::const_pointer sans modification de la sémantique.

Les exigences ci-dessus permettent de comparer les Container iterator s et const_iterator s.

Exigences de complétude de l'allocateur

Un type d'allocateur X pour le type T satisfait en outre les exigences de complétude de l'allocateur si les deux conditions suivantes sont remplies, indépendamment du fait que T soit un type complet :

  • X est un type complet.
  • À l'exception de value_type , tous les types membres de std:: allocator_traits < X > sont des types complets.
(depuis C++17)

Allocateurs avec état et sans état

Chaque Allocator est soit stateful soit stateless . Généralement, un allocateur stateful peut avoir des valeurs inégales qui désignent des ressources mémoire distinctes, tandis qu'un allocateur stateless désigne une seule ressource mémoire.

Bien que les allocateurs personnalisés ne soient pas tenus d'être sans état, l'utilisation d'allocateurs avec état dans la bibliothèque standard est définie par l'implémentation. L'utilisation de valeurs d'allocateur inégales peut entraîner des erreurs d'exécution définies par l'implémentation ou un comportement indéfini si l'implémentation ne prend pas en charge une telle utilisation.

(until C++11)

Les allocateurs personnalisés peuvent contenir un état. Chaque conteneur ou autre objet sensible aux allocateurs stocke une instance de l'allocateur fourni et contrôle le remplacement d'allocateur via std::allocator_traits .

(since C++11)

Les instances d'un type d'allocateur sans état se comparent toujours égales. Les types d'allocateurs sans état sont généralement implémentés comme des classes vides et conviennent pour l'optimisation de classe de base vide .

Le type membre is_always_equal de std::allocator_traits est intentionnellement utilisé pour déterminer si un type d'allocateur est sans état.

(depuis C++11)

Pointeurs sophistiqués

Lorsque le type membre pointer n'est pas un type pointeur brut, il est communément appelé « pointeur sophistiqué » . Ces pointeurs ont été introduits pour supporter les architectures à mémoire segmentée et sont utilisés aujourd'hui pour accéder à des objets alloués dans des espaces d'adressage différents de l'espace d'adressage virtuel homogène accessible par les pointeurs bruts. Un exemple de pointeur sophistiqué est le pointeur indépendant du mapping d'adresses boost::interprocess::offset_ptr , qui permet d'allouer des structures de données nodales telles que std::set dans la mémoire partagée et les fichiers mappés en mémoire situés à des adresses différentes dans chaque processus. Les pointeurs sophistiqués peuvent être utilisés indépendamment de l'allocateur qui les a fournis , via le modèle de classe std::pointer_traits (depuis C++11) . La fonction std::to_address peut être utilisée pour obtenir un pointeur brut à partir d'un pointeur sophistiqué. (depuis C++20)

L'utilisation de pointeurs sophistiqués et de tailles personnalisées/de types différents dans la bibliothèque standard est conditionnellement prise en charge. Les implémentations peuvent exiger que les types membres pointer , const_pointer , size_type , et difference_type soient respectivement value_type* , const value_type * , std::size_t , et std::ptrdiff_t .

(jusqu'à C++11)

Concept

Pour la définition de l'objet de requête std::get_allocator , le concept suivant, uniquement à titre d'exposition, est défini.

template < class Alloc >

concept /*simple-allocator*/ = requires ( Alloc alloc, std:: size_t n )
{
{ * alloc. allocate ( n ) } - > std:: same_as < typename Alloc :: value_type & > ;
{ alloc. deallocate ( alloc. allocate ( n ) , n ) } ;
} && std:: copy_constructible < Alloc >

&& std:: equality_comparable < Alloc > ;

Le concept uniquement à titre d'exposition /*simple-allocator*/ définit les contraintes minimales d'utilisabilité de l'exigence Allocator .

(depuis C++26)

Bibliothèque standard

Les composants suivants de la bibliothèque standard satisfont aux Allocator exigences :

l'allocateur par défaut
(modèle de classe)
implémente un allocateur multi-niveaux pour les conteneurs multi-niveaux
(modèle de classe)
un allocateur qui prend en charge le polymorphisme à l'exécution basé sur la std::pmr::memory_resource avec laquelle il est construit
(modèle de classe)

Exemples

Démontre un allocateur C++11, à l'exception de [[ nodiscard ]] ajouté pour correspondre au style C++20.

#include <cstdlib>
#include <iostream>
#include <limits>
#include <new>
#include <vector>
template<class T>
struct Mallocator
{
    typedef T value_type;
    Mallocator() = default;
    template<class U>
    constexpr Mallocator(const Mallocator <U>&) noexcept {}
    [[nodiscard]] T* allocate(std::size_t n)
    {
        if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))
            throw std::bad_array_new_length();
        if (auto p = static_cast<T*>(std::malloc(n * sizeof(T))))
        {
            report(p, n);
            return p;
        }
        throw std::bad_alloc();
    }
    void deallocate(T* p, std::size_t n) noexcept
    {
        report(p, n, 0);
        std::free(p);
    }
private:
    void report(T* p, std::size_t n, bool alloc = true) const
    {
        std::cout << (alloc ? "Alloc: " : "Dealloc: ") << sizeof(T) * n
                  << " bytes at " << std::hex << std::showbase
                  << reinterpret_cast<void*>(p) << std::dec << '\n';
    }
};
template<class T, class U>
bool operator==(const Mallocator <T>&, const Mallocator <U>&) { return true; }
template<class T, class U>
bool operator!=(const Mallocator <T>&, const Mallocator <U>&) { return false; }
int main()
{
    std::vector<int, Mallocator<int>> v(8);
    v.push_back(42);
}

Sortie possible :

Alloc: 32 bytes at 0x2020c20
Alloc: 64 bytes at 0x2023c60
Dealloc: 32 bytes at 0x2020c20
Dealloc: 64 bytes at 0x2023c60

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement publié Comportement corrigé
LWG 179 C++98 pointer et const_pointer n'étaient pas
requis d'être comparables entre eux
requis
LWG 199 C++98 la valeur de retour de a. allocate ( 0 ) était ambiguë non spécifiée
LWG 258
( N2436 )
C++98 la relation d'égalité entre allocateurs n'était
pas requise d'être réflexive, symétrique ou transitive
requise d'être réflexive,
symétrique et transitive
LWG 274 C++98 T pouvait être un type qualifié const ou un type référence,
rendant std::allocator potentiellement mal formé [1]
ces types interdits
LWG 2016 C++11 les opérations de copie, déplacement et d'échange
d'allocateur pouvaient lever des exceptions lors de l'utilisation
requises d'être non levantes
LWG 2081 C++98
C++11
les allocateurs n'étaient pas requis de supporter l'affectation
par copie (C++98) et l'affectation par déplacement (C++11)
requis
LWG 2108 C++11 il n'y avait aucun moyen de montrer qu'un allocateur est sans état is_always_equal fourni
LWG 2263 C++11 la résolution de LWG issue 179 a été accidentellement supprimée en C++11
et non généralisée à void_pointer et const_void_pointer
restaurée et généralisée
LWG 2447 C++11 T pouvait être un type objet qualifié volatile ces types interdits
LWG 2593 C++11 le déplacement d'un allocateur pouvait modifier sa valeur modification interdite
P0593R6 C++98 allocate n'était pas requis de créer un
objet tableau dans le stockage alloué
requis
  1. Les types membres reference et const_reference de std::allocator sont définis respectivement comme T& et const T& .
    • Si T est un type référence, reference et const_reference sont mal formés car une référence à une référence ne peut être formée (la compression de références a été introduite en C++11).
    • Si T est qualifié const, reference et const_reference sont identiques, et l'ensemble de surcharges de address() est mal formé.