Pero, ¿cómo puedo saber qué tipo de asignador utiliza el contenedor, si se vuelve a enlazar internamente el asignador dada desde el parámetro de plantilla?
oferta siempre un Allocator<T>
al constructor (donde T
es el value_type
del recipiente). El contenedor lo convertirá en un Allocator<U>
es necesario donde U
es una estructura de datos interna del contenedor.El Allocator
se requiere para suministrar dichos constructores de conversión, por ejemplo .:
template <class T> class allocator {
...
template <class U> allocator(const allocator<U>&);
Además leí que C++ 11 ahora utiliza asignadores de ámbito que permiten Para reutilizar el asignador de un recipiente para sus recipientes que contienen.
Bueno, para ser más precisos, C++ 11 tiene un adaptador asignador llamada scoped_allocator_adaptor
:
template <class OuterAlloc, class... InnerAllocs>
class scoped_allocator_adaptor : public OuterAlloc
{
...
};
De C++ 11:
La plantilla de clase es scoped_allocator_adaptor
una plantilla de asignador que especifica el recurso de memoria (el asignador externo) que va a ser utilizado por un contenedor (como hace cualquier otro asignador) y también especifica un recurso interno de asignación que se pasará al constructor de cada elemento dentro del contenedor. Este adaptador está instanciado con uno externo y cero o más tipos internos de asignador. Si se crea una instancia con un solo tipo de asignador , el asignador interno se convierte en scoped_allocator_adaptor
, utilizando el mismo recurso del asignador para el contenedor y cada elemento dentro del contenedor y, si los elementos en sí son contenedores, cada uno de sus elementos recursivamente. Si se instancia con más de un asignador, el primer asignador es el asignador externo para el uso del contenedor, el segundo asignador se pasa a los constructores de los elementos del contenedor, y, si los elementos en sí son contenedores, el tercer asignador es pasado a los elementos elementos, y así sucesivamente. Si los contenedores están anidados a una profundidad mayor que el número de asignadores, el último asignador se utiliza repetidamente, como en el caso de un solo asignador, para cualquier recursividad restante. [Nota: scoped_allocator_adaptor
se deriva del tipo de asignador externo por lo que puede sustituirse por el asignador externo en la mayoría de las expresiones. - nota final]
lo que sólo recibe el comportamiento asignadores de ámbito si se especifica un scoped_allocator_adaptor
como el asignador de su contenedor.
¿Cómo difiere la implementación de un contenedor con asignación de ámbito de uno que no tiene conocimiento de los contenedores de ámbito?
La clave es que el contenedor se ocupa ahora con su asignador a través de una nueva clase llamada allocator_traits
lugar de tratar con el asignador directamente. Y el contenedor debe usar allocator_traits
para ciertas operaciones como construir y destruir value_type
s en el contenedor. El contenedor no debe hablar directamente con el asignador.
Por ejemplo, los asignadores de puede proporcionar un miembro de llama construct
que construirá un tipo en una dirección determinada usando los argumentos dados:
template <class T> class Allocator {
...
template<class U, class... Args>
void construct(U* p, Args&&... args);
};
Si un asignador no proporciona este miembro, allocator_traits
proporcionarán una implementación predeterminada. En cualquier caso, el contenedor debe construir todas value_type
s utilizando esta función construct
, pero usarlo a través allocator_traits
, y no usar la allocator
directamente:
allocator_traits<allocator_type>::construct(the_allocator, *ugly details*);
El scoped_allocator_adaptor
proporciona encargo construct
funciones que allocator_traits
a pasar a los que se aprovecha los rasgos uses_allocator
y pasa el asignador correcto junto al constructor value_type
. El contenedor permanece maravillosamente ignorante de estos detalles. El contenedor solo debe saber que debe construir el value_type
utilizando la función allocator_traits construct
.
Hay más detalles que el contenedor debe tener que manejar para gestionar correctamente las asignaciones con estado. Aunque estos detalles también se solucionan al hacer que el contenedor no haga suposiciones sino que obtenga todas las propiedades y comportamientos a través del allocator_traits
. El contenedor ni siquiera puede suponer que pointer
es T*
. Más bien, este tipo se encuentra al preguntar allocator_traits
de qué se trata.
En resumen, para construir un contenedor C++ 11, estudie en allocator_traits
. Y luego obtiene el comportamiento de asignación de alcance de forma gratuita cuando sus clientes usan el scoped_allocator_adaptor
.