Namespaces
Variants

C++ named requirements: Allocator

From cppreference.net
C++ named requirements

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 tipo T ,
  • a , un objeto de tipo A ,
  • B , el tipo Allocator correspondiente para algún tipo de objeto sin calificación cv U (obtenido mediante rebinding de A ),
  • 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 T obtenido mediante la expresión * p ,
  • n , un valor de tipo std:: allocator_traits < A > :: size_type .
Tipos internos
Identificador de tipo Tipo alias Requisitos
A::pointer (opcional) (no especificado) [1]
A::const_pointer (opcional) (no especificado)
A::void_pointer (opcional) (no especificado)
  • Satisface NullablePointer .
  • A::pointer es convertible a A::void_pointer .
  • B::void_pointer y A::void_pointer son el mismo tipo.
A::const_void_pointer (opcional) (no especificado)
  • Satisface NullablePointer .
  • A::pointer , A::const_pointer , y A::void_pointer son convertibles a A::const_void_pointer .
  • B::const_void_pointer y A::const_void_pointer son el mismo tipo.
A::value_type T
A::size_type (opcional) (no especificado)
  • Un tipo entero sin signo.
  • Puede representar el tamaño del objeto más grande que A puede asignar.
A::difference_type (opcional) (no especificado)
  • Un tipo entero con signo.
  • Puede representar la diferencia de dos punteros cualesquiera a los objetos asignados por A .
A::template rebind<U>::other
(opcional) [2]
B
  • Para cualquier U , B::template rebind<T>::other es A .
Operaciones sobre punteros
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á)
Operaciones de almacenamiento y duración
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.
Relación entre instancias
Expresión Tipo de retorno Requisitos
a1 == a2 bool
  • true solo si el almacenamiento asignado por el asignador a1 puede ser liberado a través de a2 .
  • Establece una relación reflexiva, simétrica y transitiva.
  • No lanza excepciones.
a1 ! = a2
  • Igual que ! ( 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 .)
  • No lanza excepciones.
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.)
  • No lanza excepciones.
A a1 ( std :: move ( a ) ) Construye a1 de modo que sea igual al valor previo de a .
  • No lanza excepciones.
  • El valor de a permanece inalterado y a1 == a .
A a1 = std :: move ( a )
A a ( std :: move ( b ) ) Construye a de modo que sea igual al valor previo de A ( b ) .
  • No lanza excepciones.
Tipo-id Tipo alias Requisitos
A::is_always_equal
(opcional)
std::true_type o std::false_type o derivado de estos.
Influencia en las operaciones del contenedor
Expresión Tipo de retorno Descripción
a. select_on_container_copy_construction ( )
(opcional)
A
  • Proporciona una instancia de A para ser utilizada por el contenedor que se construye por copia del que actualmente usa a .
  • (Normalmente retorna una copia de a o un A construido por defecto.)
Tipo-id Tipo alias Descripción
A::propagate_on_container_copy_assignment
(opcional)
std::true_type o std::false_type o derivado de estos.
  • std::true_type o derivado de él si el asignador de tipo A necesita ser copiado cuando el contenedor que lo usa es asignado por copia.
  • Si este miembro es std::true_type o derivado de él, entonces A debe satisfacer CopyAssignable y la operación de copia no debe lanzar excepciones.
  • Nótese que si los asignadores de los contenedores fuente y destino no son iguales, la asignación por copia debe desasignar la memoria del destino usando el asignador antiguo y luego asignarla usando el nuevo asignador antes de copiar los elementos (y el asignador).
A::propagate_on_container_move_assignment
(opcional)
  • std::true_type o derivado de él si el asignador de tipo A necesita ser movido cuando el contenedor que lo usa es asignado por movimiento.
  • Si este miembro es std::true_type o derivado de él, entonces A debe satisfacer MoveAssignable y la operación de movimiento no debe lanzar excepciones.
  • Si este miembro no se proporciona o deriva de std::false_type y los asignadores de los contenedores fuente y destino no son iguales, la asignación por movimiento no puede tomar posesión de la memoria fuente y debe asignar por movimiento o construir por movimiento los elementos individualmente, redimensionando su propia memoria según sea necesario.
A::propagate_on_container_swap
(opcional)
  • std::true_type o derivado de él si los asignadores de tipo A necesitan ser intercambiados cuando dos contenedores que los usan son intercambiados.
  • Si este miembro es std::true_type o derivado de él, el tipo A debe satisfacer Swappable y la operación de intercambio no debe lanzar excepciones.
  • Si este miembro no se proporciona o deriva de std::false_type y los asignadores de los dos contenedores no son iguales, el comportamiento del intercambio de contenedores es indefinido.

Notas:

  1. Véase también fancy pointers a continuación.
  2. rebind es solo opcional (proporcionado por std::allocator_traits ) si este allocator es una plantilla de la forma SomeAllocator<T, Args> , donde Args es 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 , o X::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_pointer sin 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_pointer sin 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 X para el tipo T satisface adicionalmente los requisitos de completitud del asignador si se cumplen ambas condiciones siguientes, independientemente de si T es un tipo completo:

  • X es un tipo completo.
  • Excepto por value_type , todos los tipos miembro de std:: allocator_traits < X > son tipos completos.
(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 is_always_equal de std::allocator_traits está diseñado específicamente para determinar si un tipo de asignador es sin estado.

(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 pointer , const_pointer , size_type , y difference_type sean value_type* , const value_type * , std::size_t , y std::ptrdiff_t , respectivamente.

(hasta C++11)

Concepto

Para la definición del objeto de consulta std::get_allocator , se define el siguiente concepto solo para exposición.

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

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)
implementa un asignador multinivel para contenedores multinivel
(plantilla de clase)
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
  1. Los tipos miembro reference y const_reference de std::allocator se definen como T& y const T& respectivamente.
    • Si T es un tipo referencia, reference y const_reference son incorrectos porque no se puede formar una referencia a referencia ( colapso de referencias se introdujo en C++11).
    • Si T está calificado con const, reference y const_reference son iguales, y el conjunto de sobrecargas de address() es incorrecto.