que hace definicion c++ stl allocator size-t

c++ - hace - ¿Puede un size_type ser más grande que std:: size_t?



allocator definicion (7)

De §18.2 / 6

El tipo size_t es un tipo de entero sin signo definido por la implementación que es lo suficientemente grande como para contener el tamaño en bytes de cualquier objeto.

Entonces, si fuera posible asignar un objeto cuyo tamaño no puede ser representado por un size_t , la implementación no sería conforme.

Los contenedores estándar con un std::allocator tienen su size_type definido como std::size_t . Sin embargo, ¿es posible tener un asignador que asigne objetos cuyo tamaño no puede ser representado por un size_t ? En otras palabras, ¿puede un size_type ser más grande que size_t ?


Estoy seguro de que está enterrado en el estándar en alguna parte, pero la mejor descripción que he visto para size_type es de la documentación de SGI-STL. Como dije, estoy seguro de que está en el estándar, y si alguien puede señalarlo, por supuesto que sí.

Según SGI, size_type de un contenedor es:

Un tipo integral sin signo que puede representar cualquier valor no negativo del tipo de distancia del contenedor

No hace ningún reclamo que deba ser algo más que eso. En teoría, podría definir un contenedor que use uint64_t, char sin signo y cualquier otra cosa intermedia. Que está haciendo referencia al tipo de distancia del contenedor es la parte que me parece interesante, ya que ...

distance_type: tipo integral con signo utilizado para representar la distancia entre dos de los iteradores del contenedor. Este tipo debe ser el mismo que el tipo de distancia del iterador.

En realidad, esto no responde la pregunta, pero es interesante ver cómo size_type y size_t difieren (o pueden). En cuanto a su pregunta, vea (y vote) la respuesta de @AnalogFile, ya que creo que es correcta.


No necesariamente.

Supongo que por size_type te refieres al typedef dentro de la mayoría de los contenedores STL?

Si es así, solo porque size_type se agregó a todos los contenedores en lugar de simplemente usar size_t significa que STL se reserva el derecho de hacer size_type del tipo que quieran. (Por defecto, en todas las implementaciones, soy consciente de que size_type es un typedef de size_t).


Para agregar a las respuestas "estándar", también tenga en cuenta el proyecto stxxl , que se supone que puede manejar terabytes de datos mediante el almacenamiento en disco (quizás por extensión, almacenamiento en red). Consulte el encabezado de vector, por ejemplo, para la definición de size_type ( línea 731 y línea 742 ) como uint64.

Este es un ejemplo concreto del uso de contenedores con tamaños más grandes que los que la memoria puede permitirse, o que incluso el número entero del sistema puede manejar.


Sí, y esto podría ser útil en algunos casos.

Supongamos que tiene un programa que desea acceder a más almacenamiento que el que cabe en la memoria virtual. Al crear un asignador que hace referencia al almacenamiento mapeado en memoria y al mapeo según sea necesario al indirectar objetos de pointer , puede acceder arbitrariamente a grandes cantidades de memoria.

Esto sigue siendo conforme a 18.2: 6 porque size_t se define como lo suficientemente grande como para contener el tamaño de cualquier objeto, pero 17.6.3.5:2 tabla 28 define size_type como que contiene el tamaño del objeto más grande en el modelo de asignación , que no necesita ser un objeto real en el modelo de memoria C ++.

Tenga en cuenta que los requisitos en 17.6.3.5:2 tabla 28 no constituyen un requisito de que la asignación de múltiples objetos resulte en una matriz; para allocate(n) el requisito es:

La memoria se asigna para n objetos de tipo T

y para deallocate la afirmación es:

Todos T objetos n T en el área apuntada por p se deben destruir antes de esta llamada.

Área de nota, no matriz . Otro punto es 17.6.3.5:4:

Los tipos X::pointer , X::const_pointer , X::void_pointer y X::const_void_pointer cumplirán los requisitos de NullablePointer (17.6.3.3). Ningún constructor, operador de comparación, operación de copia, operación de movimiento u operación de intercambio en estos tipos deberá salir a través de una excepción. X::pointer y X::const_pointer también satisfarán los requisitos para un iterador de acceso aleatorio (24.2).

No es necesario aquí que (&*p) + n sea ​​lo mismo que p + n .

Es perfectamente legítimo que un modelo expresable dentro de otro modelo contenga objetos no representables en el modelo externo; por ejemplo, modelos no estándar en lógica matemática.


Si y no.

Como explica @AnalogFile, ninguna memoria asignada puede ser mayor que size_t . Por lo tanto, un contenedor que hereda su size_type de un asignador no puede tener size_type más grande que size_t .

Sin embargo, puede diseñar un tipo de contenedor que represente una colección que no está completamente almacenada en la memoria direccionable. Por ejemplo, los miembros podrían estar en el disco o en una base de datos. Incluso podrían calcularse dinámicamente, por ejemplo, una secuencia de Fibonacci, y nunca almacenarse en ningún lugar. En tales casos, size_type podría ser más grande que size_t .


size_t es el tipo de entero sin signo que obtienes al aplicar sizeof .

sizeof debería devolver el tamaño del tipo (o del tipo de expresión) que es su argumento. En el caso de las matrices, debe devolver el tamaño de la matriz completa.

Esto implica que:

  • no puede haber CUALQUIER estructura o unión que sea más grande que lo que size_t puede representar.

  • no puede haber ninguna matriz que sea más grande que lo que size_t puede representar.

En otras palabras, si algo encaja en el bloque más grande de memoria consecutiva al que puede acceder, entonces su tamaño debe ajustarse en size_t (en términos no portátiles, pero fáciles de comprender intuitivamente esto significa que en la mayoría de los sistemas size_t es tan grande como void* y puede "medir" la totalidad de su espacio de direcciones virtuales).

Editar: esta próxima oración probablemente sea incorrecta. Vea abajo

Por lo tanto, la respuesta a ¿ es posible tener un asignador que asigne objetos cuyo tamaño no puede ser representado por un size_t ? no es.

Editar (adendo):

He estado pensando sobre esto y lo anterior, de hecho, estoy equivocado. Revisé el estándar y parece posible diseñar un asignador completamente personalizado con tipos de puntero completamente personalizados, que incluyen el uso de diferentes tipos de puntero, puntero de const, puntero de vacío y puntero de const void. Por lo tanto, un asignador puede de hecho tener un tamaño_tipo mayor que size_t.

Pero para hacerlo, necesita definir realmente tipos de punteros completamente personalizados y las instancias correspondientes de caracteres asignador y asignador.

La razón por la que digo es que todavía no estoy del todo claro si el size_type necesita abarcar el tamaño del objeto individual o también el tamaño de varios objetos (esto es, una matriz) en el modelo del asignador. Tendré que investigar este detalle (pero no ahora, es hora de cenar aquí :))

Edit2 (nueva adición):

@larsmans, creo que es posible que desee decidir qué aceptar de todos modos. El problema parece ser un poco más complicado de lo que uno puede intuir. Estoy editando la respuesta nuevamente ya que mis pensamientos son definitivamente más que un comentario (tanto en contenido como en tamaño).

ReEdit (como se señala en los comentarios, los siguientes dos párrafos no son correctos):

En primer lugar, size_type es solo un nombre. Por supuesto, puede definir un contenedor y agregarle un size_type con el significado que desee. Tu size_type podría ser un flotador, una cadena cualquiera.

Dicho esto, en los contenedores de biblioteca estándar size_type se define en el contenedor solo para facilitar el acceso. De hecho, se supone que es idéntico al size_type del asignador para ese contenedor (y el size_type del asignador debe ser el size_type de los allotator_traits de ese asignador).

Por lo tanto, en lo sucesivo supondremos que el size_type del contenedor, incluso uno que usted defina, sigue la misma lógica "por convención". @BenVoight comienza su respuesta con "Como explica @AnalogFile, ninguna memoria asignada puede ser mayor que size_t. Por lo tanto, un contenedor que hereda su size_type de un asignador no puede tener size_type mayor que size_t". De hecho, ahora estamos estipulando que si un contenedor tiene un size_type , eso proviene del asignador (dice heredar, pero eso por supuesto no está en el sentido común de la herencia de clase).

Sin embargo, puede o no ser 100% correcto que un size_type (incluso si proviene de un asignador) está necesariamente limitado a size_t . La pregunta realmente es: ¿puede un asignador (y los rasgos correspondientes) definir un size_type que sea más grande que size_t ?

Tanto @BenVoight como @ecatmur sugieren un caso de uso donde la tienda de respaldo es un archivo. Sin embargo, si la tienda de respaldo es un archivo solo para el contenido y usted tiene algo en la memoria que se refiere a ese contenido (vamos a llamarlo ''mango''), entonces de hecho está haciendo un contenedor que contiene controladores. Un manejador será una instancia de alguna clase que almacena los datos reales en un archivo y solo conserva en la memoria lo que necesita para recuperar esos datos, pero esto es irrelevante para el contenedor: el contenedor almacenará los identificadores y esos están en la memoria y todavía estamos en el espacio de direcciones "normal", por lo que mi respuesta inicial sigue siendo válida.

Hay otro caso, sin embargo. No está asignando controladores, en realidad está almacenando cosas en el archivo (o base de datos) y su asignador (y rasgos relativos) definen puntero, const pointer, void pointer, const void puntero, etc. tipos que administran directamente ese almacén de respaldo. En este caso, por supuesto, también necesitan definir size_type (reemplazando size_t ) y difference_type (reemplazando ptrdiff_t) para que coincida.

Las dificultades directas para definir size_type (y difference_type ) como mayor que size_t cuando size_t ya es tan grande como la implementación más grande proporcionada tipo integral primitiva (si no, entonces no hay dificultades) están relacionadas con el hecho de que deben ser integer types .

Dependiendo de cómo interpreta el estándar, esto puede ser imposible (porque de acuerdo con los integer types estándar son los tipos definidos en el estándar más los extended integer types proporcionados por la implementación) o posible (si lo interpreta de tal manera que puede proporcionar una extended integer type usted mismo) siempre que pueda escribir una clase que se comporte exactamente como un tipo primitivo. Esto era imposible en los viejos tiempos (las reglas de sobrecarga hicieron que los tipos primitivos siempre se distinguieran de los tipos definidos por el usuario), pero no estoy 100% actualizado con C ++ 11 y esto puede (o no) cambiarse.

Sin embargo, también hay dificultades indirectas. No solo necesita proporcionar un tipo de entero adecuado para size_type . También debe proporcionar el resto de la interfaz del asignador.

He estado pensando un poco y un problema que veo es implementar *p acuerdo con 17.6.3.5. En esa *p sintaxis p es un pointer tipeado por los rasgos del asignador. Por supuesto, podemos escribir una clase y definir un operator* (la versión del método nullary, haciendo puntero dereferece). Y uno puede pensar que esto se puede hacer fácilmente ''localizando'' la parte relativa del archivo (como sugiere @ecatmur). Sin embargo, hay un problema: *p debe ser una T& para ese objeto. Por lo tanto, el objeto en sí debe caber en la memoria y, lo que es más importante, dado que puede hacer T &ref = *p y mantener esa referencia indefinidamente, una vez que haya localizado los datos, nunca más podrá paginarlos. Esto significa que efectivamente no puede haber una manera adecuada de implementar dicho asignador a menos que toda la tienda de respaldo también pueda cargarse en la memoria.

Esas son mis primeras observaciones y parecen confirmar mi primera impresión de que la verdadera respuesta es no: no hay una forma práctica de hacerlo.

Sin embargo, como puede ver, las cosas son mucho más complicadas de lo que parece sugerir la mera intuición. Puede llevar bastante tiempo encontrar una respuesta definitiva (y puedo o no seguir investigando el tema).

Por el momento solo diré: parece que no es posible . Las declaraciones en contrario solo serán aceptables si no se basan únicamente en la intuición: código postal y permita a la gente debatir si su código cumple totalmente con 17.6.3.5 y si su size_type (que será mayor que size_t incluso si size_t es tan grande como el tipo entero primitivo más grande) se puede considerar un tipo entero.