Может ли size_type быть больше, чем std :: size_t?

Стандартные контейнеры с std::allocator иметь их size_type определяется как std::size_t. Однако возможно ли иметь распределитель, который выделяет объекты, размер которых не может быть представлен size_t? Другими словами, может ли size_type когда-либо будет больше, чем size_t?

36

Решение

Да, и это может быть полезно в некоторых случаях.

Предположим, у вас есть программа, которая хочет получить доступ к большему объему памяти, чем умещается в виртуальной памяти. Создав распределитель, который ссылается на хранилище, отображенное в памяти, и отображает его так, как требуется при косвенном pointer объекты, вы можете получить доступ к произвольно большим объемам памяти.

Это остается в соответствии с 18.2: 6, потому что size_t определяется как достаточно большой, чтобы содержать размер любого объекта, но 17.6.3.5:2 таблица 28 определяет size_type как содержащий размер самый большой объект в модели размещения, которые не должны быть реальным объектом в модели памяти C ++.

Обратите внимание, что требования в таблице 28 17.6.3.5:2 не составляют требования о том, что выделение нескольких объектов должно приводить к массиву; за allocate(n) Требование:

Память выделена для n объекты типа T

и для deallocate утверждение таково:

Все n T объекты в области
указал на p должен быть
уничтожен до этого звонка.

Заметка площадь, не массив. Другой момент — 17.6.3.5:4:

X::pointer, X::const_pointer, X::void_pointer, а также X::const_void_pointer типы должны удовлетворять
требования NullablePointer (17.6.3.3). Нет конструктора, оператор сравнения, операция копирования,
Операция перемещения или операция обмена на этих типах должна завершаться через исключение. X::pointer а также X::const_pointer также должен удовлетворять требованиям для итератора произвольного доступа (24.2).

Здесь не требуется (&*p) + n должен быть таким же, как p + n,

Вполне допустимо, чтобы модель, выражаемая в другой модели, содержала объекты, не представляемые во внешней модели; например, нестандартные модели в математической логике.

25

Другие решения

size_t тип целого числа без знака, полученного при применении sizeof,

sizeof должен вернуть размер типа (или типа выражения), который является его аргументом. В случае массивов он должен возвращать размер всего массива.

Это подразумевает, что:

  • не может быть ЛЮБОЙ структуры или объединения, которое больше чем size_t может представлять.

  • не может быть никакого массива, который больше чем size_t может представлять.

Другими словами, если что-то помещается в самый большой блок последовательной памяти, к которому вы можете получить доступ, то его размер должен соответствовать size_t (в непереносимых, но интуитивно понятных терминах, это означает, что в большинстве систем size_t столь же большой как void* и может «измерить» все ваше виртуальное адресное пространство).

Изменить: это следующее предложение, вероятно, неправильно. Увидеть ниже

Поэтому ответ на возможно ли иметь распределитель, который выделяет объекты, размер которых не может быть представлен size_t? нет.

Изменить (приложение):

Я думал об этом, и все вышесказанное может быть на самом деле неправильно. Я проверил стандарт, и кажется возможным разработать полностью собственный распределитель с полностью настраиваемыми типами указателей, включая использование различных типов для указателя, указателя констант, указателя void и указателя const void. Следовательно, распределитель может на самом деле иметь size_type, который больше, чем size_t.

Но для этого вам нужно фактически определить полностью настраиваемые типы указателей и соответствующие им экземпляры свойств-распределителей и распределителей.

Причина, по которой я говорю может в том, что мне все еще немного неясно, size_type должен охватывать размер отдельного объекта или также размер нескольких объектов (то есть массив) в модели распределителя. Мне нужно будет изучить эту деталь (но не сейчас, здесь время обеда :))

Edit2 (новое приложение):

@larsmans Я думаю, вы все равно можете решить, что принять. Проблема, кажется, немного сложнее, чем можно понять интуитивно. Я снова редактирую ответ, так как мои мысли определенно больше, чем комментарий (как по содержанию, так и по размеру).

ReEdit (как указано в комментариях, следующие два абзаца не верны):

Прежде всего size_type это просто имя. Конечно, вы можете определить контейнер и добавить size_type к этому с любым значением, которое вы хотите. Ваш size_type может быть как поплавок, строка как угодно.

Что сказано в стандартных контейнерах библиотеки size_type определяется в контейнере только для облегчения доступа. На самом деле он должен быть идентичен size_type распределителя для этого контейнера (и size_type распределитель должен быть size_type из allotator_traits этого распределителя).

Поэтому мы и впредь будем предполагать, что size_type контейнера, даже тот, который вы определяете, следует той же логике «по соглашению». @BenVoight начинает свой ответ с «@AnalogFile объясняет, что выделенная память не может быть больше, чем size_t. Поэтому контейнер, который наследует свой size_type от распределителя, не может иметь size_type больше, чем size_t.». На самом деле мы сейчас оговариваем, что если контейнер имеет size_type тогда это исходит от распределителя (он говорит наследовать, но это, конечно, не в общем смысле наследования классов).

Однако он может или не может быть на 100% прав, что size_type (даже если он исходит от распределителя) обязательно ограничен size_t, Вопрос на самом деле: может ли распределитель (и соответствующие черты) определить size_type это больше чем size_t?

И @BenVoight, и @ecatmur предлагают вариант использования, в котором резервным хранилищем является файл. Однако, если хранилище резервных копий является файлом только для контента, и у вас есть что-то в памяти, которое ссылается на этот контент (назовем это «дескриптором»), то вы фактически создаете контейнер, содержащий дескрипторы. Дескриптор будет экземпляром некоторого класса, который хранит фактические данные в файле и сохраняет в памяти только то, что ему нужно для извлечения этих данных, но это не имеет отношения к контейнеру: контейнер будет хранить дескрипторы, а те находятся в памяти и мы все еще находимся в «нормальном» адресном пространстве, поэтому мой первоначальный ответ остается в силе.

Однако есть еще один случай. Вы не выделяете дескрипторы, вы на самом деле храните вещи в файле (или базе данных), а ваш распределитель (и относительные черты) определяют указатель, указатель const, указатель void, указатель const void и т. Д., Которые непосредственно управляют этим резервным хранилищем. В этом случае, конечно, они также должны определить size_type (замена size_t) а также difference_type (заменяя ptrdiff_t), чтобы соответствовать.

Прямые трудности в определении size_type (а также difference_type) больше чем size_t когда size_t уже настолько велика, насколько большая реализация обеспечена примитивным целочисленным типом (если нет, то нет никаких трудностей) связаны с тем, что они должны быть integer types,

В зависимости от того, как вы интерпретируете стандарт, это может быть невозможно (потому что согласно стандарту integer types типы определены в стандарте плюс extended integer types предоставляется реализацией) или возможно (если вы интерпретируете это так, что вы можете предоставить extended integer type себя) до тех пор, пока вы можете написать класс, который ведет себя именно так как примитивный тип. Это было невозможно в старые времена (правила перегрузки делали примитивные типы всегда отличимыми от пользовательских типов), но я не на 100% в курсе C ++ 11, и это может (или не может быть изменено).

Однако есть и косвенные трудности. Вам нужно не только предоставить подходящий целочисленный тип для size_type, Вы также должны предоставить остальную часть интерфейса распределителя.

Я думал об этом немного, и я вижу одну проблему в реализации *p в соответствии с 17.6.3.5. В этом *p синтаксис p это pointer как набрано чертами распределителя. Конечно, мы можем написать класс и определить operator* (версия нулевого метода, делающая разыменование указателя). И можно подумать, что это легко сделать, «вставив» в относительную часть файла (как предполагает @ecatmur). Однако есть проблема: *p должен быть T& для этого объекта. Поэтому сам объект должен уместиться в памяти и, что более важно, так как вы можете сделать T &ref = *p и удерживайте эту ссылку неограниченное время, после того как вы разместите данные, вам больше не будет разрешено их выводить на страницу. Это означает, что эффективно не может быть способа правильно реализовать такой распределитель, если только целое резервное хранилище также не может быть загружено в память.

Это мои ранние наблюдения, и, похоже, они подтверждают мое первое впечатление, что реальный Ответ — нет: практического способа сделать это не существует.

Однако, как вы видите, все гораздо сложнее, чем кажется, подсказывает простая интуиция. Может потребоваться некоторое время, чтобы найти окончательный ответ (и я могу или не могу идти дальше и исследовать тему дальше).

На данный момент я просто скажу: кажется невозможным. Заявления об обратном должны быть приемлемы только в том случае, если они не основаны исключительно на интуиции: постите код и позволяйте людям спорить, полностью ли ваш код соответствует 17.6.3.5 и если ваш size_type (который должен быть больше чем size_t даже если size_t равный самому большому примитивному целому типу) может рассматриваться как целочисленный тип.

19

И да и нет.

Как объясняет @AnalogFile, выделенная память не может быть больше, чем size_t, Таким образом, контейнер, который наследует его size_type из распределителя не может иметь size_type больше, чем size_t,

Однако вы можете создать тип контейнера, который представляет коллекцию, которая не полностью хранится в адресуемой памяти. Например, члены могут быть на диске или в базе данных. Они даже могут быть вычислены динамически, например, последовательность Фибоначчи, и никогда нигде не хранится вообще. В таких случаях, size_type может быть больше, чем size_t,

15

Я уверен, что это где-то похоронено в стандарте, но лучшее описание, которое я видел для size_type из документации SGI-STL. Как я уже сказал, я уверен, что это в стандарте, и если кто-то может указать на это, во что бы то ни стало.

Согласно SGI, тип контейнера size_type:

Целочисленный тип без знака, который может представлять любое неотрицательное значение
тип расстояния контейнера

Он не претендует на то, что должно быть что-то помимо этого. Теоретически вы можете определить контейнер, который использует uint64_t, unsigned char и все остальное между ними. То, что он ссылается на тип_пределения контейнера, является частью, которую я нахожу интересной, так как …

distance_type: целочисленный тип со знаком, используемый для представления расстояния
между двумя итераторами контейнера. Этот тип должен быть одинаковым
как тип расстояния итератора.

Это на самом деле не отвечает на этот вопрос, но интересно посмотреть, как size_type и size_t различаются (или могут). Что касается вашего вопроса, см. (И до голосования) ответ @AnalogFile, так как я считаю, что он правильный.

5

Из §18.2 / 6

Тип size_t является целочисленным типом целочисленного типа, определяемым реализацией, который достаточно велик, чтобы содержать размер в байтах любого объекта.

Итак, если бы вы могли выделить объект, размер которого не может быть представлен size_t это сделало бы реализацию несоответствующей.

3

Чтобы добавить к «стандартным» ответам, также обратите внимание на stxxl проект, который должен обрабатывать терабайты данных с использованием дискового хранилища (возможно, по расширению, сетевого хранилища). Увидеть заголовок вектора например, для определения size_type (строка 731, а также строка 742) как uint64.

Это конкретный пример использования контейнеров большего размера, чем может себе позволить память, или с которыми может справиться даже целое число системы.

1

Не обязательно.

Я предполагаю, что под size_type вы подразумеваете typedef внутри большинства контейнеров STL?

Если так, то просто потому, что size_type был добавлен во все контейнеры
просто использование size_t означает, что STL оставляет за собой право сделать
size_type любой тип, который им нравится. (По умолчанию во всех реализациях я знаю
size_type — это typedef для size_t).

0