C++ named requirements: Allocator
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 TypT, -
a
, ein Objekt vom Typ
A, -
B, der entsprechende Allocator -Typ für einen cv-unqualifizierten ObjekttypU(erhalten durch Rebinding vonA), -
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 .
| Typ-ID | Alias-Typ | Anforderungen |
|---|---|---|
A::pointer
(optional)
|
(nicht spezifiziert) [1] |
|
A::const_pointer
(optional)
|
(nicht spezifiziert) |
|
A::void_pointer
(optional)
|
(nicht spezifiziert) |
|
A::const_void_pointer
(optional)
|
(nicht spezifiziert) |
|
A::value_type
|
T
|
|
A::size_type
(optional)
|
(nicht spezifiziert) |
|
A::difference_type
(optional)
|
(nicht spezifiziert) |
|
A::template rebind<U>::other
(optional) [2] |
B
|
|
| 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) |
| 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.
|
| Ausdruck | Rückgabetyp | Anforderungen |
|---|---|---|
| a1 == a2 | bool |
|
| a1 ! = a2 |
|
|
| Deklaration | Effekt | Anforderungen |
| A a1 ( a ) |
Kopierkonstruiert
a1
so, dass
a1
==
a
.
(Hinweis: Jeder Allocator erfüllt ebenfalls CopyConstructible .) |
|
| 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.)
|
|
| A a1 ( std :: move ( a ) ) | Konstruiert a1 so, dass es dem vorherigen Wert von a entspricht. |
|
| A a1 = std :: move ( a ) | ||
| A a ( std :: move ( b ) ) | Konstruiert a so, dass es dem vorherigen Wert von A ( b ) entspricht. |
|
| Typ-ID | Alias-Typ | Anforderungen |
A::is_always_equal
(optional) |
std::true_type oder std::false_type oder davon abgeleitet. |
|
| Ausdruck | Rückgabetyp | Beschreibung |
|---|---|---|
|
a.
select_on_container_copy_construction
(
)
(optional) |
A
|
|
| Typ-ID | Alias-Typ | Beschreibung |
A::propagate_on_container_copy_assignment
(optional) |
std::true_type oder std::false_type oder davon abgeleitet. |
|
A::propagate_on_container_move_assignment
(optional) |
|
|
A::propagate_on_container_swap
(optional) |
|
Hinweise:
- ↑ Siehe auch fancy pointers unten.
-
↑
rebindist nur optional (bereitgestellt durch std::allocator_traits ), wenn dieser Allocator ein Template der FormSomeAllocator<T, Args>ist, wobeiArgsnull 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, oderX::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_pointerkonvertiert 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_pointerersetzt 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_pointerersetzt 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
|
(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
|
(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
|
(until C++11) |
KonzeptFür die Definition des Abfrageobjekts std::get_allocator wird das folgende ausschließlich darstellende Konzept definiert.
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) |
|
|
(C++11)
|
implementiert mehrstufigen Allokator für mehrstufige Container
(Klassen-Template) |
|
(C++17)
|
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 |
-
↑
Die Member-Typen
referenceundconst_referencevon std::allocator sind definiert alsT&bzw.const T&.-
Wenn
Tein Referenztyp ist, sindreferenceundconst_referencefehlerhaft, da keine Referenz auf eine Referenz gebildet werden kann ( Reference Collapsing wurde in C++11 eingeführt). -
Wenn
Tconst-qualifiziert ist, sindreferenceundconst_referenceidentisch, und der Überladungssatz von address() ist fehlerhaft.
-
Wenn