Namespaces
Variants

C++ named requirements: Allocator

From cppreference.net
C++ named requirements

Kapselt Strategien für den Zugriff/Adressierung, Allokierung/Deallokierung und Konstruktion/Destruktion von Objekten.

Jede Standardbibliothekskomponente, die Speicher allozieren oder freigeben muss, von std::string , std::vector und jedem Container , außer std::array (seit C++11) und std::inplace_vector (seit C++26) , bis zu std::shared_ptr und std::function (bis C++17) , tut dies durch einen Allocator : ein Objekt eines Klassentyps, das die folgenden Anforderungen erfüllt.

Die Implementierung vieler Allocator-Anforderungen ist optional, da alle AllocatorAwareContainer auf Allokatoren indirekt über std::allocator_traits zugreifen, und std::allocator_traits die Standardimplementierung dieser Anforderungen bereitstellt.

Inhaltsverzeichnis

Anforderungen

Gegeben

  • T , ein nicht-konstanter, nicht-Referenz-Typ (bis C++11) nicht-konstanter Objekttyp (seit C++11) (bis C++17) cv-unqualifizierter Objekttyp (seit C++17) ,
  • A , ein Allocator -Typ für Typ T ,
  • a , ein Objekt vom Typ A ,
  • B , der entsprechende Allocator -Typ für einen cv-unqualifizierten Objekttyp U (erhalten durch Rebinding von A ),
  • b , ein Objekt vom Typ B ,
  • p , ein Wert vom Typ std:: allocator_traits < A > :: pointer , erhalten durch Aufruf von std:: allocator_traits < A > :: allocate ( ) ,
  • cp , ein Wert vom Typ std:: allocator_traits < A > :: const_pointer , erhalten durch Konvertierung von p ,
  • vp , ein Wert vom Typ std:: allocator_traits < A > :: void_pointer , erhalten durch Konvertierung von p ,
  • cvp , ein Wert vom Typ std:: allocator_traits < A > :: const_void_pointer , erhalten durch Konvertierung von cp oder von vp ,
  • xp , ein dereferenzierbarer Zeiger auf einen cv-unqualifizierten Objekttyp X ,
  • r , ein Lvalue vom Typ T , erhalten durch den Ausdruck * p ,
  • n , ein Wert vom Typ std:: allocator_traits < A > :: size_type .
Innere Typen
Typ-ID Alias-Typ Anforderungen
A::pointer (optional) (nicht spezifiziert) [1]
A::const_pointer (optional) (nicht spezifiziert)
A::void_pointer (optional) (nicht spezifiziert)
  • Erfüllt NullablePointer .
  • A::pointer ist konvertierbar zu A::void_pointer .
  • B::void_pointer und A::void_pointer sind derselbe Typ.
A::const_void_pointer (optional) (nicht spezifiziert)
  • Erfüllt NullablePointer .
  • A::pointer , A::const_pointer , und A::void_pointer sind konvertierbar zu A::const_void_pointer .
  • B::const_void_pointer und A::const_void_pointer sind derselbe Typ.
A::value_type T
A::size_type (optional) (nicht spezifiziert)
  • Ein vorzeichenloser Ganzzahltyp.
  • Kann die Größe des größten Objekts darstellen, das A allozieren kann.
A::difference_type (optional) (nicht spezifiziert)
  • Ein vorzeichenbehafteter Ganzzahltyp.
  • Kann die Differenz zweier beliebiger Zeiger auf durch A allozierte Objekte darstellen.
A::template rebind<U>::other
(optional) [2]
B
  • Für jedes U ist B::template rebind<T>::other gleich A .
Operationen auf Zeigern
Ausdruck Rückgabetyp Anforderungen
* p T&
* cp const T & * cp und * p identifizieren dasselbe Objekt.
p - > m (wie angegeben) Gleich wie ( * p ) . m , falls ( * p ) . m wohldefiniert ist.
cp - > m (wie angegeben) Gleich wie ( * cp ) . m , falls ( * cp ) . m wohldefiniert ist.
static_cast < A :: pointer > ( vp ) (wie angegeben) static_cast < A :: pointer > ( vp ) == p
static_cast < A :: const_pointer > ( cvp ) (wie angegeben) static_cast < A :: const_pointer > ( cvp ) == cp
std:: pointer_traits < A :: pointer > :: pointer_to ( r ) (wie angegeben)
Speicher- und Lebensdaueroperationen
Ausdruck Rückgabetyp Anforderungen
a. allocate ( n ) A::pointer Reserviert Speicher für ein Array-Objekt vom Typ T[n] und erstellt das Array, konstruiert jedoch keine Array-Elemente. Kann Ausnahmen werfen. Wenn n == 0 , ist der Rückgabewert nicht spezifiziert.
a. allocate ( n, cvp ) (optional) Gleich wie a. allocate ( n ) , kann jedoch cvp ( nullptr oder einen Zeiger von a. allocate ( ) ) auf nicht spezifizierte Weise zur Lokalitätsunterstützung verwenden.
a. allocate_at_least ( n ) (optional) (since C++23) std:: allocation_result

< A :: pointer >

Reserviert Speicher für ein Array-Objekt vom Typ T[cnt] und erstellt das Array, konstruiert jedoch keine Array-Elemente, gibt dann { p, cnt } zurück, wobei p auf den Speicher zeigt und cnt nicht kleiner als n ist. Kann Ausnahmen werfen.
a. deallocate ( p, n ) (nicht verwendet) Gibt den von p referenzierten Speicher frei, der ein Rückgabewert eines vorherigen Aufrufs von allocate oder allocate_at_least (since C++23) sein muss, der nicht durch einen zwischenzeitlichen Aufruf von deallocate ungültig gemacht wurde. n muss mit dem zuvor an allocate übergebenen Wert übereinstimmen oder zwischen der angeforderten und zurückgegebenen Elementanzahl von allocate_at_least liegen (kann gleich einer der Grenzen sein) (since C++23) . Wirft keine Ausnahmen.
a. max_size ( ) (optional) A::size_type Der größte Wert, der an A :: allocate ( ) übergeben werden kann.
a. construct ( xp, args... ) (optional) (nicht verwendet) Konstruiert ein Objekt vom Typ X im zuvor reservierten Speicher an der von xp referenzierten Adresse unter Verwendung von args... als Konstruktorargumente.
a. destroy ( xp ) (optional) (nicht verwendet) Zerstört ein Objekt vom Typ X , auf das xp zeigt, gibt jedoch keinen Speicher frei.
Beziehung zwischen Instanzen
Ausdruck Rückgabetyp Anforderungen
a1 == a2 bool
  • true nur dann, wenn der durch den Allokator a1 reservierte Speicher durch a2 freigegeben werden kann.
  • Stellt reflexive, symmetrische und transitive Beziehung her.
  • Wirft keine Ausnahmen.
a1 ! = a2
  • Gleichbedeutend mit ! ( a1 == a2 ) .
Deklaration Effekt Anforderungen
A a1 ( a ) Kopierkonstruiert a1 so, dass a1 == a .
(Hinweis: Jeder Allocator erfüllt ebenfalls CopyConstructible .)
  • Wirft keine Ausnahmen.
A a1 = a
A a ( b ) Konstruiert a so, dass B ( a ) == b und A ( b ) == a .
(Hinweis: Dies impliziert, dass alle durch rebind verbundenen Allokatoren die Ressourcen des anderen beibehalten, wie z.B. Speicherpools.)
  • Wirft keine Ausnahmen.
A a1 ( std :: move ( a ) ) Konstruiert a1 so, dass es dem vorherigen Wert von a entspricht.
  • Wirft keine Ausnahmen.
  • Der Wert von a bleibt unverändert und a1 == a .
A a1 = std :: move ( a )
A a ( std :: move ( b ) ) Konstruiert a so, dass es dem vorherigen Wert von A ( b ) entspricht.
  • Wirft keine Ausnahmen.
Typ-ID Alias-Typ Anforderungen
A::is_always_equal
(optional)
std::true_type oder std::false_type oder davon abgeleitet.
Einfluss auf Container-Operationen
Ausdruck Rückgabetyp Beschreibung
a. select_on_container_copy_construction ( )
(optional)
A
  • Stellt eine Instanz von A bereit, die von dem Container verwendet wird, der aus demjenigen kopierkonstruiert wird, der aktuell a verwendet.
  • (Gibt normalerweise entweder eine Kopie von a oder einen standardkonstruierten A zurück.)
Typ-ID Alias-Typ Beschreibung
A::propagate_on_container_copy_assignment
(optional)
std::true_type oder std::false_type oder davon abgeleitet.
  • std::true_type oder davon abgeleitet, wenn der Allokator vom Typ A kopiert werden muss, wenn der Container, der ihn verwendet, kopierzugewiesen wird.
  • Wenn dieses Mitglied std::true_type oder davon abgeleitet ist, dann muss A die Anforderung CopyAssignable erfüllen und der Kopiervorgang darf keine Ausnahmen werfen.
  • Hinweis: Wenn sich die Allokatoren von Quell- und Zielcontainer nicht als gleich vergleichen, muss die Kopierzuweisung den Speicher des Ziels mit dem alten Allokator freigeben und dann mit dem neuen Allokator allozieren, bevor die Elemente (und der Allokator) kopiert werden.
A::propagate_on_container_move_assignment
(optional)
  • std::true_type oder davon abgeleitet, wenn der Allokator vom Typ A verschoben werden muss, wenn der Container, der ihn verwendet, verschobenzugewiesen wird.
  • Wenn dieses Mitglied std::true_type oder davon abgeleitet ist, dann muss A die Anforderung MoveAssignable erfüllen und der Verschiebevorgang darf keine Ausnahmen werfen.
  • Wenn dieses Mitglied nicht bereitgestellt wird oder von std::false_type abgeleitet ist und sich die Allokatoren von Quell- und Zielcontainer nicht als gleich vergleichen, kann die Verschiebezuweisung nicht den Besitz des Quellspeichers übernehmen und muss die Elemente einzeln verschiebezuweisen oder verschiebekonstruieren, wobei der eigene Speicher bei Bedarf neu dimensioniert wird.
A::propagate_on_container_swap
(optional)
  • std::true_type oder davon abgeleitet, wenn die Allokatoren vom Typ A ausgetauscht werden müssen, wenn zwei Container, die sie verwenden, ausgetauscht werden.
  • Wenn dieses Mitglied std::true_type oder davon abgeleitet ist, muss der Typ A die Anforderung Swappable erfüllen und der Austauschvorgang darf keine Ausnahmen werfen.
  • Wenn dieses Mitglied nicht bereitgestellt wird oder von std::false_type abgeleitet ist und sich die Allokatoren der beiden Container nicht als gleich vergleichen, ist das Verhalten des Container-Austauschs undefiniert.

Hinweise:

  1. Siehe auch fancy pointers unten.
  2. rebind ist nur optional (bereitgestellt durch std::allocator_traits ), wenn dieser Allocator ein Template der Form SomeAllocator<T, Args> ist, wobei Args null oder mehr zusätzliche Template-Typparameter sind.

Gegeben

  • x1 und x2 , Objekte von (möglicherweise unterschiedlichen) Typen X::void_pointer , X::const_void_pointer , X::pointer , oder X::const_pointer
Dann sind x1 und x2 äquivalent-wertige Zeigerwerte, genau dann wenn sowohl x1 als auch x2 explizit in die beiden entsprechenden Objekte px1 und px2 vom Typ X::const_pointer konvertiert werden können, unter Verwendung einer Sequenz von static_cast s, die nur diese vier Typen verwendet, und der Ausdruck px1 == px2 zu true ausgewertet wird.

Gegeben

  • w1 und w2 , Objekte vom Typ X::void_pointer
Dann kann für den Ausdruck w1 == w2 und w1 ! = w2 eines oder beide Objekte durch ein gleichwertiges Objekt vom Typ X::const_void_pointer ersetzt werden, ohne dass sich die Semantik ändert.

Gegeben

  • p1 und p2 , Objekte vom Typ X::pointer
Dann können für die Ausdrücke p1 == p2 , p1 ! = p2 , p1 < p2 , p1 <= p2 , p1 >= p2 , p1 > p2 , p1 - p2 eines oder beide Objekte durch ein äquivalentwertiges Objekt vom Typ X::const_pointer ersetzt werden, ohne dass sich die Semantik ändert.

Die oben genannten Anforderungen ermöglichen es, Container - iterator s und const_iterator s zu vergleichen.

Anforderungen an die Allokator-Vollständigkeit

Ein Allokator-Typ X für den Typ T erfüllt zusätzlich die Anforderungen an die Allokator-Vollständigkeit , wenn beide der folgenden Punkte unabhängig davon erfüllt sind, ob T ein vollständiger Typ ist:

  • X ist ein vollständiger Typ.
  • Mit Ausnahme von value_type sind alle Mitgliedstypen von std:: allocator_traits < X > vollständige Typen.
(seit C++17)

Zustandsbehaftete und zustandslose Allokatoren

Jeder Allocator -Typ ist entweder stateful oder stateless . Allgemein kann ein stateful Allocator-Typ ungleiche Werte haben, die unterschiedliche Speicherressourcen bezeichnen, während ein stateless Allocator-Typ eine einzelne Speicherressource bezeichnet.

Obwohl benutzerdefinierte Allokatoren nicht zustandslos sein müssen, ist die Verwendung von zustandsbehafteten Allokatoren in der Standardbibliothek implementierungsdefiniert. Die Verwendung ungleicher Allokatorwerte kann zu implementierungsdefinierten Laufzeitfehlern oder undefiniertem Verhalten führen, falls die Implementierung solche Verwendungen nicht unterstützt.

(until C++11)

Benutzerdefinierte Allokatoren können Zustand enthalten. Jeder Container oder ein anderes allokatorbewusstes Objekt speichert eine Instanz des bereitgestellten Allokators und steuert den Allokatoraustausch über std::allocator_traits .

(since C++11)

Instanzen eines zustandslosen Allokatortyps vergleichen immer gleich. Zustandslose Allokatortypen werden typischerweise als leere Klassen implementiert und eignen sich für Empty-Base-Class-Optimierung .

Der Member-Typ is_always_equal von std::allocator_traits wird gezielt verwendet, um zu bestimmen, ob ein Allokator-Typ zustandslos ist.

(since C++11)

Fancy-Pointer

Wenn der Membertyp pointer kein Rohzeigertyp ist, wird er üblicherweise als "fancy pointer" bezeichnet. Solche Zeiger wurden zur Unterstützung segmentierter Speicherarchitekturen eingeführt und werden heute verwendet, um auf Objekte in Adressräumen zuzugreifen, die sich vom homogenen virtuellen Adressraum unterscheiden, auf den Rohzeiger zugreifen. Ein Beispiel für einen fancy pointer ist der adressunabhängige Abbildungszeiger boost::interprocess::offset_ptr , der es ermöglicht, knotenbasierte Datenstrukturen wie std::set in Shared Memory und Speicher-abbildbaren Dateien zu allozieren, die in jedem Prozess unter unterschiedlichen Adressen abgebildet sind. Fancy pointers können unabhängig vom Allokator verwendet werden, der sie bereitgestellt hat , durch das Klassentemplate std::pointer_traits (seit C++11) . Die Funktion std::to_address kann verwendet werden, um einen Rohzeiger aus einem fancy pointer zu erhalten. (seit C++20)

Die Verwendung von speziellen Zeigern und angepassten Größen/unterschiedlichen Typen in der Standardbibliothek ist bedingt unterstützt. Implementierungen können verlangen, dass die Member-Typen pointer , const_pointer , size_type und difference_type jeweils value_type* , const value_type * , std::size_t und std::ptrdiff_t sind.

(until C++11)

Konzept

Für die Definition des Abfrageobjekts std::get_allocator wird das folgende ausschließlich darstellende Konzept definiert.

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 > ;

Das ausschließlich darstellende Konzept /*simple-allocator*/ definiert die minimalen Nutzbarkeitsanforderungen der Allocator -Anforderung.

(seit C++26)

Standardbibliothek

Die folgenden Standardbibliothekskomponenten erfüllen die Allocator -Anforderungen:

der Standard-Allokator
(Klassen-Template)
implementiert mehrstufigen Allokator für mehrstufige Container
(Klassen-Template)
ein Allokator, der Laufzeit-Polymorphie basierend auf der std::pmr::memory_resource unterstützt, mit der er konstruiert wurde
(Klassen-Template)

Beispiele

Demonstriert einen C++11-Allocator, abgesehen von [[ nodiscard ]] hinzugefügt, um dem C++20-Stil zu entsprechen.

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

Mögliche Ausgabe:

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

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
LWG 179 C++98 pointer und const_pointer mussten nicht
miteinander vergleichbar sein
erforderlich
LWG 199 C++98 der Rückgabewert von a. allocate ( 0 ) war unklar ist nicht spezifiziert
LWG 258
( N2436 )
C++98 die Gleichheitsbeziehung zwischen Allokatoren musste
nicht reflexiv, symmetrisch oder transitiv sein
muss reflexiv,
symmetrisch und transitiv sein
LWG 274 C++98 T konnte ein const-qualifizierter Typ oder Referenztyp sein,
was std::allocator möglicherweise fehlerhaft machte [1]
diese Typen verboten
LWG 2016 C++11 die Kopier-, Verschiebe- und Austauschoperationen von
Allokatoren könnten beim Einsatz Ausnahmen werfen
müssen ausnahmefrei sein
LWG 2081 C++98
C++11
Allokatoren mussten keine Zuweisung per Kopie
(C++98) und Verschiebung (C++11) unterstützen
erforderlich
LWG 2108 C++11 es gab keine Möglichkeit zu zeigen, dass ein Allokator zustandslos ist is_always_equal bereitgestellt
LWG 2263 C++11 die Lösung von LWG Issue 179 wurde in C++11 versehentlich entfernt
und nicht auf void_pointer und const_void_pointer verallgemeinert
wiederhergestellt und verallgemeinert
LWG 2447 C++11 T konnte ein volatile-qualifizierter Objekttyp sein diese Typen verboten
LWG 2593 C++11 das Verschieben von einem Allokator könnte seinen Wert verändern Veränderung verboten
P0593R6 C++98 allocate musste kein Array-Objekt im
allokierten Speicher erstellen
erforderlich
  1. Die Member-Typen reference und const_reference von std::allocator sind definiert als T& bzw. const T& .
    • Wenn T ein Referenztyp ist, sind reference und const_reference fehlerhaft, da keine Referenz auf eine Referenz gebildet werden kann ( Reference Collapsing wurde in C++11 eingeführt).
    • Wenn T const-qualifiziert ist, sind reference und const_reference identisch, und der Überladungssatz von address() ist fehlerhaft.