Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

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

Обратите внимание, что требования в таблице 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.

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

Ответ 2

size_t - это тип целых чисел без знака, которые вы получаете, применяя sizeof.

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

Это означает, что:

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

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

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

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

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

Изменить (добавление):

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

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

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

Edit2 (новое добавление):

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

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

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

В контейнерах стандартной библиотеки 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 pointer и т.д. Типы, которые непосредственно управляют этим хранилищем. В этом случае, конечно, они также должны определить size_type (заменяя size_t) и difference_type (заменяя ptrdiff_t) на соответствие.

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

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

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

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

Это мои ранние наблюдения и, похоже, на самом деле подтверждают мое первое впечатление о том, что ответ real - нет: практического способа сделать это нет.

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

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

Ответ 3

Да и нет.

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

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

Ответ 4

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

В соответствии с SGI контейнер size_type:

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

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

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

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

Ответ 5

Из §18.2/6

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

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

Ответ 6

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

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

Ответ 7

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

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

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