C++ named requirements: Allocator
Encapsula estrategias para acceso/direccionamiento, asignación/liberación y construcción/destrucción de objetos.
Cada componente de la biblioteca estándar que pueda necesitar asignar o liberar almacenamiento, desde std::string , std::vector , y cada contenedor , excepto std::array (desde C++11) y std::inplace_vector (desde C++26) , hasta std::shared_ptr y std::function (hasta C++17) , lo hace a través de un Allocator : un objeto de tipo clase que satisface los siguientes requisitos.
La implementación de muchos requisitos de asignadores es opcional porque todos los AllocatorAwareContainer acceden a los asignadores indirectamente a través de std::allocator_traits , y std::allocator_traits proporciona la implementación predeterminada de esos requisitos.
Contenidos |
Requisitos
Dado
-
T, un tipo no constante y no referencia (hasta C++11) de objeto no constante (desde C++11) (hasta C++17) de objeto sin calificación cv (desde C++17) , -
A, un tipo Allocator para el tipoT, -
a
, un objeto de tipo
A, -
B, el tipo Allocator correspondiente para algún tipo de objeto sin calificación cvU(obtenido mediante rebinding deA), -
b
, un objeto de tipo
B, - p , un valor de tipo std:: allocator_traits < A > :: pointer , obtenido llamando a std:: allocator_traits < A > :: allocate ( ) ,
- cp , un valor de tipo std:: allocator_traits < A > :: const_pointer , obtenido mediante conversión de p ,
- vp , un valor de tipo std:: allocator_traits < A > :: void_pointer , obtenido mediante conversión de p ,
- cvp , un valor de tipo std:: allocator_traits < A > :: const_void_pointer , obtenido mediante conversión de cp o de vp ,
-
xp
, un puntero dereferenciable a algún tipo de objeto sin calificación cv
X, -
r
, un valor-l de tipo
Tobtenido mediante la expresión * p , - n , un valor de tipo std:: allocator_traits < A > :: size_type .
| Identificador de tipo | Tipo alias | Requisitos |
|---|---|---|
A::pointer
(opcional)
|
(no especificado) [1] |
|
A::const_pointer
(opcional)
|
(no especificado) |
|
A::void_pointer
(opcional)
|
(no especificado) |
|
A::const_void_pointer
(opcional)
|
(no especificado) |
|
A::value_type
|
T
|
|
A::size_type
(opcional)
|
(no especificado) |
|
A::difference_type
(opcional)
|
(no especificado) |
|
A::template rebind<U>::other
(opcional) [2] |
B
|
|
| Expresión | Tipo de retorno | Requisitos |
|---|---|---|
| * p |
T&
|
|
| * cp | const T & | * cp y * p identifican el mismo objeto. |
| p - > m | (como está) | Igual que ( * p ) . m , si ( * p ) . m está bien definido. |
| cp - > m | (como está) | Igual que ( * cp ) . m , si ( * cp ) . m está bien definido. |
| static_cast < A :: pointer > ( vp ) | (como está) | static_cast < A :: pointer > ( vp ) == p |
| static_cast < A :: const_pointer > ( cvp ) | (como está) | static_cast < A :: const_pointer > ( cvp ) == cp |
| std:: pointer_traits < A :: pointer > :: pointer_to ( r ) | (como está) |
| Expresión | Tipo de retorno | Requisitos |
|---|---|---|
| a. allocate ( n ) |
A::pointer
|
Asigna almacenamiento adecuado para un objeto array de tipo
T[n]
y crea el array, pero no construye los elementos del array. Puede lanzar excepciones. Si
n
==
0
, el valor de retorno no está especificado.
|
| a. allocate ( n, cvp ) (opcional) | Igual que a. allocate ( n ) , pero puede usar cvp ( nullptr o un puntero obtenido de a. allocate ( ) ) de manera no especificada para ayudar a la localidad. | |
| a. allocate_at_least ( n ) (opcional) (desde C++23) |
std::
allocation_result
< A :: pointer > |
Asigna almacenamiento adecuado para un objeto array de tipo
T[cnt]
y crea el array, pero no construye los elementos del array, luego retorna
{
p, cnt
}
, donde
p
apunta al almacenamiento y
cnt
no es menor que
n
. Puede lanzar excepciones.
|
| a. deallocate ( p, n ) | (no se usa) |
Desasigna el almacenamiento apuntado por
p
, que debe ser un valor retornado por una llamada previa a
allocate
o
allocate_at_least
(desde C++23)
que no haya sido invalidado por una llamada intermedia a
deallocate
.
n
debe coincidir con el valor pasado previamente a
allocate
o estar entre la solicitud y el número de elementos retornado mediante
allocate_at_least
(puede ser igual a cualquiera de los límites)
(desde C++23)
. No lanza excepciones.
|
| a. max_size ( ) (opcional) |
A::size_type
|
El valor más grande que puede pasarse a A :: allocate ( ) . |
| a. construct ( xp, args... ) (opcional) | (no se usa) |
Construye un objeto de tipo
X
en almacenamiento previamente asignado en la dirección apuntada por
xp
, usando
args...
como argumentos del constructor.
|
| a. destroy ( xp ) (opcional) | (no se usa) |
Destruye un objeto de tipo
X
apuntado por
xp
, pero no desasigna ningún almacenamiento.
|
| Expresión | Tipo de retorno | Requisitos |
|---|---|---|
| a1 == a2 | bool |
|
| a1 ! = a2 |
|
|
| Declaración | Efecto | Requisitos |
| A a1 ( a ) |
Construye por copia
a1
de modo que
a1
==
a
.
(Nota: Todo Allocator también satisface CopyConstructible .) |
|
| A a1 = a | ||
| A a ( b ) |
Construye
a
de modo que
B
(
a
)
==
b
y
A
(
b
)
==
a
.
(Nota: Esto implica que todos los asignadores relacionados por
rebind
mantienen los recursos mutuos, como grupos de memoria.)
|
|
| A a1 ( std :: move ( a ) ) | Construye a1 de modo que sea igual al valor previo de a . |
|
| A a1 = std :: move ( a ) | ||
| A a ( std :: move ( b ) ) | Construye a de modo que sea igual al valor previo de A ( b ) . |
|
| Tipo-id | Tipo alias | Requisitos |
A::is_always_equal
(opcional) |
std::true_type o std::false_type o derivado de estos. |
|
| Expresión | Tipo de retorno | Descripción |
|---|---|---|
|
a.
select_on_container_copy_construction
(
)
(opcional) |
A
|
|
| Tipo-id | Tipo alias | Descripción |
A::propagate_on_container_copy_assignment
(opcional) |
std::true_type o std::false_type o derivado de estos. |
|
A::propagate_on_container_move_assignment
(opcional) |
|
|
A::propagate_on_container_swap
(opcional) |
|
Notas:
- ↑ Véase también fancy pointers a continuación.
-
↑
rebindes solo opcional (proporcionado por std::allocator_traits ) si este allocator es una plantilla de la formaSomeAllocator<T, Args>, dondeArgses cero o más parámetros de tipo de plantilla adicionales.
Dado
-
x1
y
x2
, objetos de tipos (posiblemente diferentes)
X::void_pointer,X::const_void_pointer,X::pointer, oX::const_pointer
-
Entonces,
x1
y
x2
son valores de puntero
equivalentes
, si y solo si tanto
x1
como
x2
pueden convertirse explícitamente a los dos objetos correspondientes
px1
y
px2
de tipo
X::const_pointer, usando una secuencia de static_cast s usando solo estos cuatro tipos, y la expresión px1 == px2 se evalúa como true .
Dado
-
w1
y
w2
, objetos de tipo
X::void_pointer
-
Entonces, para la expresión
w1
==
w2
y
w1
!
=
w2
cualquiera o ambos objetos pueden ser reemplazados por un
objeto de valor equivalente
de tipo
X::const_void_pointersin cambio en la semántica.
Dado
-
p1
y
p2
, objetos de tipo
X::pointer
-
Entonces, para las expresiones
p1
==
p2
,
p1
!
=
p2
,
p1
<
p2
,
p1
<=
p2
,
p1
>=
p2
,
p1
>
p2
,
p1
-
p2
uno o ambos objetos pueden ser reemplazados por un objeto
de valor equivalente
de tipo
X::const_pointersin cambio en la semántica.
Los requisitos anteriores hacen posible comparar los
Container
iterator
s y
const_iterator
s.
Requisitos de completitud del asignador
Un tipo de asignador
|
(desde C++17) |
Asignadores con estado y sin estado
Cada tipo de Allocator es stateful o stateless . Generalmente, un tipo de allocator stateful puede tener valores desiguales que denotan recursos de memoria distintos, mientras que un tipo de allocator stateless denota un único recurso de memoria.
|
Aunque los asignadores personalizados no requieren ser sin estado, el uso de asignadores con estado en la biblioteca estándar es definido por la implementación. El uso de valores de asignadores desiguales puede resultar en errores de tiempo de ejecución definidos por la implementación o comportamiento indefinido si la implementación no soporta dicho uso. |
(until C++11) |
|
Los asignadores personalizados pueden contener estado. Cada contenedor u otro objeto consciente de asignadores almacena una instancia del asignador suministrado y controla el reemplazo del asignador a través de std::allocator_traits . |
(since C++11) |
Las instancias de un tipo de asignador sin estado siempre son iguales al compararse. Los tipos de asignadores sin estado normalmente se implementan como clases vacías y son adecuados para la optimización de clase base vacía .
|
El tipo miembro
|
(desde C++11) |
Punteros sofisticados
Cuando el tipo miembro
pointer
no es un tipo de puntero crudo, comúnmente se denomina
"fancy pointer"
. Estos punteros se introdujeron para admitir arquitecturas de memoria segmentada y se utilizan actualmente para acceder a objetos asignados en espacios de dirección que difieren del espacio de direcciones virtual homogéneo al que se accede mediante punteros crudos. Un ejemplo de fancy pointer es el puntero independiente de dirección de mapeo
boost::interprocess::offset_ptr
, que permite asignar estructuras de datos basadas en nodos como
std::set
en memoria compartida y archivos mapeados en memoria asignados en direcciones diferentes en cada proceso. Los fancy pointers pueden utilizarse independientemente del asignador que los proporcionó
, mediante la plantilla de clase
std::pointer_traits
(desde C++11)
.
La función
std::to_address
puede utilizarse para obtener un puntero crudo desde un fancy pointer.
(desde C++20)
|
El uso de punteros sofisticados y tamaños/tipos personalizados en la biblioteca estándar tiene soporte condicional. Las implementaciones pueden requerir que los tipos miembro
|
(hasta C++11) |
ConceptoPara la definición del objeto de consulta std::get_allocator , se define el siguiente concepto solo para exposición.
El concepto solo para exposición /*simple-allocator*/ define las restricciones mínimas de usabilidad del requisito Allocator . |
(desde C++26) |
Biblioteca estándar
Los siguientes componentes de la biblioteca estándar satisfacen los Allocator requisitos:
|
el asignador por defecto
(plantilla de clase) |
|
|
(C++11)
|
implementa un asignador multinivel para contenedores multinivel
(plantilla de clase) |
|
(C++17)
|
un asignador que soporta polimorfismo en tiempo de ejecución basado en el
std::pmr::memory_resource
con el que se construye
(plantilla de clase) |
Ejemplos
Demuestra un asignador de C++11, excepto por
[[
nodiscard
]]
añadido para coincidir con el estilo de 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); }
Salida posible:
Alloc: 32 bytes at 0x2020c20 Alloc: 64 bytes at 0x2023c60 Dealloc: 32 bytes at 0x2020c20 Dealloc: 64 bytes at 0x2023c60
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| LWG 179 | C++98 |
pointer
y
const_pointer
no estaban
requeridos para ser comparables entre sí |
requerido |
| LWG 199 | C++98 | el valor de retorno de a. allocate ( 0 ) no estaba claro | no especificado |
|
LWG 258
( N2436 ) |
C++98 |
la relación de igualdad entre allocators
no estaba requerida para ser reflexiva, simétrica o transitiva |
requerida para ser reflexiva,
simétrica y transitiva |
| LWG 274 | C++98 |
T
podría ser un tipo calificado const o tipo referencia,
haciendo que std::allocator posiblemente esté mal formado [1] |
prohibidos estos tipos |
| LWG 2016 | C++11 |
las operaciones de copia, movimiento e intercambio
del allocator podrían lanzar excepciones cuando se usaban |
requeridas para ser no lanzadoras |
| LWG 2081 |
C++98
C++11 |
los allocators no estaban requeridos para soportar asignación
por copia (C++98) y asignación por movimiento (C++11) |
requerido |
| LWG 2108 | C++11 | no había forma de mostrar que un allocator es sin estado |
is_always_equal
proporcionado
|
| LWG 2263 | C++11 |
la resolución de
LWG issue 179
fue eliminada accidentalmente en C++11
y no generalizada a
void_pointer
y
const_void_pointer
|
restaurada y generalizada |
| LWG 2447 | C++11 |
T
podría ser un tipo objeto calificado volatile
|
prohibidos estos tipos |
| LWG 2593 | C++11 | mover desde un allocator podría modificar su valor | modificación prohibida |
| P0593R6 | C++98 |
allocate
no estaba requerido para crear un
objeto array en el almacenamiento que asignaba |
requerido |
-
↑
Los tipos miembro
referenceyconst_referencede std::allocator se definen comoT&yconst T&respectivamente.-
Si
Tes un tipo referencia,referenceyconst_referenceson incorrectos porque no se puede formar una referencia a referencia ( colapso de referencias se introdujo en C++11). -
Si
Testá calificado con const,referenceyconst_referenceson iguales, y el conjunto de sobrecargas de address() es incorrecto.
-
Si