36 votes

Un size_type peut-il jamais être plus grand que std :: size_t?

Les conteneurs standard avec std::allocator ont leur size_type défini comme std::size_t . Cependant, est-il possible d'avoir un allocateur qui alloue des objets dont la taille ne peut pas être représentée par un size_t ? En d'autres termes, un size_type peut-il jamais être supérieur à size_t ?

25voto

ecatmur Points 64173

Oui, et cela pourrait être utile dans certains cas.

Supposons que vous avez un programme qui souhaite accéder à plus d'espace de stockage que s'inscrira dans la mémoire virtuelle. Par la création d'un allocateur références mappés en mémoire de stockage et de la cartographie comme nécessaire lorsque indirecting pointer objets, vous pouvez accéder de manière arbitraire de grandes quantités de mémoire.

Cela reste conforme à 18,2:6 car size_t est défini comme assez grand pour contenir la taille de n'importe quel objet, mais 17.6.3.5:2 tableau 28 définit size_type comme contenant la taille de l'objet le plus grand dans le modèle d'allocation, qui n'a pas besoin d'être un objet réel dans le C++ modèle de mémoire.

Notez que les exigences de l'17.6.3.5:2 tableau 28 ne constituent pas une exigence que l'attribution de plusieurs objets de résultat dans un tableau; pour allocate(n) , la condition est:

La mémoire est allouée pour l' n objets de type T

et pour deallocate l'affirmation est:

Tous n T des objets dans la zone de pointée par p doit être détruit avant cet appel.

Note de la zone, pas de tableau. Un autre point est 17.6.3.5:4:

L' X::pointer, X::const_pointer, X::void_pointer, et X::const_void_pointer types de satisfaire les exigences de NullablePointer (17.6.3.3). Aucun constructeur, opérateur de comparaison, l'opération de copie, opération de déplacement, ou de swap opération sur ces types de sortie au moyen d'une exception. X::pointer et X::const_pointer doit également satisfaire aux exigences d'un itérateur à accès aléatoire (24.2).

Il n'est pas nécessaire ici qu' (&*p) + n devrait être le même que p + n.

Il est parfaitement légitime pour un modèle exprimable dans un autre modèle pour contenir des objets non représentable à l'extérieur du modèle; par exemple, les modèles standard de la logique mathématique.

19voto

Analog File Points 3539

size_t est le type des entiers non signés, vous obtenez en appliquant sizeof.

sizeof doit renvoyer la taille du type (ou le type de l'expression) qui est son argument. Dans le cas des tableaux, il doit renvoyer la taille de l'ensemble de la baie.

Ceci implique que:

  • il n'y a pas de structure ou de l'union qui est plus grand que ce que l' size_t peut représenter.

  • il n'y a pas de tableau qui est plus grand que ce qu' size_t peut représenter.

En d'autres termes, si quelque chose s'inscrit dans le plus grand bloc consécutives de la mémoire, vous pouvez accéder, puis sa taille doit tenir dans un size_t (non-portable, mais facile à comprendre intuitivement, cela signifie que sur la plupart des systèmes d' size_t est aussi grand que void* et peut "mesurer" l'ensemble de votre espace d'adressage virtuel).

Edit: cette phrase est probablement faux. Voir ci-dessous

Donc la réponse est-il possible d'avoir un allocateur qui alloue des objets dont la taille ne peut pas être représenté par un size_t? est non.

Edit (additif):

J'ai pensé à elle et au-dessus de mon être en fait mal. J'ai vérifié la norme et il semble être possible de concevoir un allocateur personnalisé totalement personnalisé de type pointeur, y compris à l'aide de différents types de pointeur, pointeur const, nulle pointeur et const void pointeur. Par conséquent, un allocateur peut en fait avoir un size_type qui est plus grande que size_t.

Mais pour ce faire vous avez besoin pour réellement définir complètement personnalisé de type pointeur et le correspondant de l'allocation et l'allocation des traits instances.

La raison pour laquelle je dis peut , c'est que je suis toujours un peu difficile de savoir si l' size_type des besoins pour étendre la taille de l'objet unique ou aussi de la taille de plusieurs objets (c'est un tableau) dans l'allocateur de modèle. J'ai besoin d'enquêter sur ce détail (mais pas maintenant, il est temps de dîner ici :) )

Edit2 (nouvel avenant):

@larsmans je pense que vous pouvez décider ce qu'il faut accepter de toute façon. Le problème semble être un peu plus compliqué que l'on pourrait intuitivement réaliser. Je suis en train de modifier la réponse, là encore, que mes pensées sont définitivement plus qu'un commentaire (à la fois dans le contenu et dans la taille).

Remontez (comme l'a fait remarquer dans les commentaires les deux paragraphes qui suivent ne sont pas correctes):

Tout d'abord size_type est juste un nom. Bien sûr, vous pouvez définir un récipient et ajouter un size_type avec quel que soit le sens que vous souhaitez. Votre size_type pourrait être un nombre, une chaîne de quoi que ce soit.

Cela dit dans les conteneurs de la bibliothèque standard size_type est défini dans le conteneur uniquement pour le rendre facile d'accès. C'est en fait censé être identique à l' size_type de l'allocateur pour que le conteneur (et l' size_type de l'allocation devrait être l' size_type de la allotator_traits de l'allocateur).

Par conséquent, nous allons désormais supposer que l' size_type du récipient, même celui que vous définissez, suit la même logique "par convention". @BenVoight commence sa réponse "avec @AnalogFile explique, pas de mémoire allouée peut être plus grand que size_t. Si un conteneur qui hérite de sa size_type à partir d'un allocateur ne peut pas avoir size_type plus grande que size_t.". En fait, nous sommes maintenant en stipulant que si un conteneur a un size_type alors que vient de l'allocateur (dit-il hériter, mais ce n'est pas dans le bon sens de l'héritage de classe).

Cependant, il peut ou peut ne pas être à 100% que size_type (même si elle vient d'un allocateur) est nécessairement limitée à size_t. La question est vraiment: un allocateur (et les traits) définir un size_type qui est plus grand que size_t?

Les deux @BenVoight et @ecatmur suggèrent un cas d'utilisation où la mémoire de sauvegarde est un fichier. Toutefois, si la mémoire de sauvegarde est un fichier seulement pour le contenu et vous avez quelque chose dans la mémoire qui fait référence à ce contenu (appelons cela une "poignée"), alors vous êtes en fait un conteneur qui contient les poignées. Une poignée être une instance d'une classe qui stocke les données réelles sur un fichier et ne conserve en mémoire ce qu'il a besoin de récupérer les données, mais ce n'est pas pertinent pour le récipient, celui-ci va stocker les poignées et ceux qui sont dans la mémoire et nous sommes encore dans la "normale" de l'espace d'adresse, donc, ma première réaction est toujours valide.

Il existe un autre cas, cependant. Vous n'êtes pas l'allocation de poignées, vous êtes en fait ranger des trucs dans le fichier (ou base de données) et votre allocateur (et par rapport traits) définir un pointeur const pointeur, pointeur void, const void pointeur etc. les types qui gèrent directement que de mémoire de sauvegarde. Dans ce cas, bien sûr, ils ont aussi besoin de définir l' size_type (remplacer size_t) et difference_type (en remplacement de ptrdiff_t) pour correspondre.

Le direct des difficultés dans la définition d' size_type (et difference_type) comme plus grand que size_t lorsque size_t est déjà aussi grand que le plus grand de la mise en œuvre à condition primitive de type intégral (si non, alors il n'y a pas de difficultés) sont liées au fait qu'ils doivent être integer types.

Selon la façon dont vous interprétez la norme, ce sera peut-être impossible (parce que, selon la norme integer types sont les types définis dans la norme en plus de la extended integer types prévus par la mise en œuvre) ou possible (si vous l'interpréter de telle sorte que vous pouvez fournir un extended integer type vous-même) aussi longtemps que vous pouvez écrire une classe qui se comporte exactement comme un type primitif. C'était impossible dans les temps anciens (surcharge de règles fait faire des types primitifs toujours la distinction de types définis par l'utilisateur), mais je ne suis pas à 100% up-to-date avec C++11 et cela peut (ou ne peut pas être changé).

Cependant, il existe aussi indirecte des difficultés. Vous n'avez pas seulement besoin de fournir un type entier pour size_type. Vous devez également fournir le reste de l'allocateur de l'interface.

J'ai réfléchis un peu et le seul problème que je vois est dans la mise en oeuvre *p selon 17.6.3.5. Dans cette *p de la syntaxe p est pointer telle que saisie par l'allocateur de traits. Bien sûr, nous pouvons écrire une classe et de définir un operator* (le nullary méthode de version, en faisant pointeur dereferece). Et on peut penser que cela peut être facilement fait par des "pagination dans" de la part relative du fichier (comme @ecatmur l'indique). Toutefois, il y a un problème: *p doit être un T& pour cet objet. Par conséquent, l'objet lui-même devez tenir en mémoire et, plus important encore, puisque vous pouvez le faire T &ref = *p et de garder la référence indéfiniment, une fois que vous avez paginée dans les données que vous ne sera jamais permis à la page il rien de plus. Cela signifie qu'effectivement il peut y avoir aucun moyen de mettre correctement en œuvre d'une telle allocation, sauf si l'ensemble de la mémoire de sauvegarde peut également être chargé en mémoire.

Ce sont mes premières observations et semblent effectivement confirmer ma première impression que la vraie réponse est non: il n'y a aucun moyen pratique de le faire.

Cependant, comme vous le voyez, les choses sont beaucoup plus complexes que la simple intuition semble le suggérer. Il peut prendre plus de temps pour trouver une réponse définitive (et je peut ou peut ne pas aller de l'avant et de recherche le sujet plus loin).

Pour l'instant, je vais juste dire: il ne semble pas être possible. Les déclarations contraires ne sont acceptés s'ils ne sont pas uniquement basées sur l'intuition: code postal et laisser les gens se débat si votre code entièrement conforme à 17.6.3.5 et si votre size_type (qui doit être supérieure à size_t même si size_t est la plus grande primitive de type entier) peut être considéré comme un type entier.

15voto

Ben Voigt Points 151460

Oui et non.

Comme @AnalogFile explique, pas de mémoire allouée peut être plus grand que size_t. Si un conteneur qui hérite de sa size_type d'un allocateur ne peut pas avoir d' size_type de plus de size_t.

Cependant, vous pouvez concevoir un type de conteneur qui représente une collection pas entièrement stockée dans la mémoire adressable. Par exemple, les membres pourraient être sur le disque ou dans une base de données. Ils pourraient même être calculé dynamiquement, par exemple, une séquence de Fibonacci, et jamais stockées n'importe où. Dans de tels cas, size_type pourrait facilement être plus grand que size_t.

5voto

WhozCraig Points 32734

Je suis sûr que ses enterré dans la norme, quelque part, mais la meilleure description que j'ai vu pour size_type est de la SGI-STL documentation. Comme je l'ai dit, je suis sûr que c'est dans la norme, et si quelqu'un peut indiquer, par tous les moyens de le faire.

Selon les SIG, un conteneur size_type est:

Un unsigned type intégral qui peut représenter n'importe quelle positif de la valeur de le conteneur du type de distance

Il ne prétend pas que c'est doit être quelque chose d'ailleurs que. En théorie, vous pourriez définir un conteneur qui utilise un uint64_t, unsigned char, et rien d'autre entre les. Qu'il est de référencement du conteneur distance_type est la partie que je trouve intéressant, depuis...

distance_type: signé intégrale type utilisé pour représenter la distance entre deux des conteneur des itérateurs. Ce type doit être le même comme l'itérateur de type de distance.

Ce n'est pas vraiment répondre à la question, si, mais il est intéressant de voir comment size_type et size_t diffèrent (ou peut). Concernant votre question, voir (et voter) @AnalogFile s réponse, car je crois que c'est correct.

3voto

Praetorian Points 47122

À partir de §18.2 / 6

Le type size_t est un type entier non signé défini par l'implémentation et suffisamment grand pour contenir la taille en octets de tout objet.

Donc, s'il était possible pour vous d'allouer un objet dont la taille ne peut pas être représentée par un size_t , l'implémentation serait non conforme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X