37

带有 的标准容器std::allocator定义size_typestd::size_t. 但是,是否可以有一个分配器来分配大小不能用 a 表示的对象size_t?换句话说,asize_type可以大于size_t吗?

4

7 回答 7

25

是的,这在某些情况下可能很有用。

假设您有一个程序希望访问比虚拟内存容量更大的存储空间。通过创建一个引用内存映射存储的分配器并在间接pointer对象时根据需要对其进行映射,您可以访问任意大量的内存。

这仍然符合 18.2:6,因为size_t它被定义为足够大以包含任何对象的大小,但 17.6.3.5:2 表 28 定义size_type为包含分配模型中最大对象的大小,它不必是实际对象在 C++ 内存模型中。

请注意,17.6.3.5:2 表 28 中的要求并不构成分配多个对象应导致数组的要求;因为allocate(n)要求是:

n为类型的对象分配内存T

对于deallocate断言是:

n T指向的区域中的所有对象p都应在此调用之前销毁。

注意区域,而不是数组。还有一点是17.6.3.5:4:

X::pointerX::const_pointer和类型应满足 NullablePointer (17.6.3.3) 的要求X::void_pointerX::const_void_pointer对这些类型的构造函数、比较运算符、复制操作、移动操作或交换操作均不得通过异常退出。X::pointer并且X::const_pointer还应满足随机访问迭代器(24.2)的要求。

这里没有要求(&*p) + np + n.

一个可在另一个模型中表示的模型包含在外部模型中不可表示的对象是完全合法的;例如,数理逻辑中的非标准模型。

于 2012-09-19T16:54:06.703 回答
19

size_t是您通过应用获得的无符号整数的类型sizeof

sizeof应该返回作为他的参数的类型(或表达式的类型)的大小。如果是数组,它应该返回整个数组的大小。

这意味着:

  • 不能有任何结构或联合大于size_t可以表示的内容。

  • 不能有任何数组大于size_t可以表示的数组。

换句话说,如果某些东西适合您可以访问的最大连续内存块,那么它的大小必须适合 size_t(在不可移植但易于直观理解的术语中,这意味着在大多数系统size_tvoid*, “测量”整个虚拟地址空间)。

编辑:下一句可能是错误的。见下文

因此,答案是否有可能有一个分配器来分配大小不能用 a 表示的对象size_t没有。

编辑(附录):

我一直在考虑它,上面我实际上是错误的。我检查了标准,似乎可以设计一个具有完全自定义指针类型的完全自定义分配器,包括对指针、const 指针、void 指针和 const void 指针使用不同的类型。因此,分配器实际上可以具有大于 size_t 的 size_type。

但要做到这一点,您实际上需要定义完全自定义的指针类型以及相应的分配器和分配器特征实例。

我说可能的原因是我仍然有点不清楚是否size_type需要跨越分配器模型中单个对象的大小或多个对象(即数组)的大小。我需要调查这个细节(但不是现在,这里是晚餐时间:))

Edit2(新附录):

@larsmans 我想你可能想决定无论如何要接受什么。这个问题似乎比人们直观地意识到的要复杂一些。我正在再次编辑答案,因为我的想法绝对不仅仅是评论(内容和大小)。

重新编辑(正如评论中指出的,接下来的两段不正确):

首先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_typethen 来自分配器(他说继承,但这当然不是类继承的常识)。

然而,他可能 100% 正确,也可能不会 100% 正确,即 a size_type(即使它来自分配器)必然被限制为size_t。真正的问题是:分配器(和相应的特征)可以定义size_type大于的 asize_t吗?

@BenVoight 和 @ecatmur 都建议使用后备存储是文件的用例。但是,如果后备存储是仅用于内容的文件,并且您在内存中有一些引用该内容的内容(我们称其为“句柄”),那么您实际上是在做一个包含句柄的容器。句柄将是某个类的实例,它将实际数据存储在文件中,并且仅将检索该数据所需的任何内容保存在内存中,但这与容器无关:容器将存储句柄,而这些句柄在内存中我们仍然在“正常”地址空间中,所以我的初始响应仍然有效。

然而,还有另一种情况。您没有分配句柄,您实际上是在文件(或数据库)中存储内容,并且您的分配器(和相关特征)定义直接管理该后备存储的指针、常量指针、无效指针、常量无效指针等类型。在这种情况下,当然,他们还需要定义size_type(replacing size_t) 和difference_type(replacing ptrdiff_t) 来匹配。

size_type将(and difference_type) 定义为大于size_twhen的直接困难size_t已经与提供的原始整数类型的最大实现一样大(如果不是,则没有困难)与它们需要是 的事实有关integer types

取决于您如何解释标准,这可能是不可能的(因为根据标准integer types是标准中定义的类型加上实现extended integer types提供的类型)或可能的(如果您解释它以便您extended integer type自己提供),只要您可以编写一个行为与原始类型完全一样的类。这在过去是不可能的(重载规则确实使原始类型始终与用户定义的类型区分开来),但我不是 100% 与 C++11 保持同步,这可能(或可能不会改变)。

但是也有间接的困难。您不仅需要为size_type. 您还需要提供分配器接口的其余部分。

我一直在考虑它,我看到的一个问题是*p根据 17.6.3.5 实施。在该*p语法p中是 apointer由分配器特征键入。当然我们可以写一个类并定义一个operator*(空方法版本,做指针解引用)。并且有人可能认为这可以通过“分页”文件的相关部分来轻松完成(正如@ecatmur 所建议的那样)。但是有一个问题:*p必须是T&该对象的 a 。因此,对象本身必须适合内存,更重要的是,因为您可能会这样做T &ref = *p并无限期地保留该引用,一旦您对数据进行了分页,您将不再被允许将其分页。这意味着除非整个后备存储也可以加载到内存中,否则可能无法有效地正确实现这样的分配器。

这些是我早期的观察,似乎实际上证实了我的第一印象,即真正的答案是否定的:没有实际的方法可以做到这一点。

然而,正如你所看到的,事情比直觉所暗示的要复杂得多。找到一个明确的答案可能需要相当长的时间(我可能会也可能不会继续研究这个话题)。

目前我只想说:这似乎是不可能的。相反的陈述只有在不完全基于直觉的情况下才可以接受:发布代码并让人们辩论您的代码是否完全符合 17.6.3.5 以及您的size_type(which shall be greater than size_teven if size_tis as large as the maximum primitive整数类型)可以被认为是整数类型。

于 2012-09-19T16:41:51.927 回答
15

是和不是。

正如@AnalogFile 解释的那样,分配的内存不能大于size_t. 因此,size_type从分配器继承的容器不能size_type大于size_t.

但是,您可以设计一个容器类型来表示不完全存储在可寻址内存中的集合。例如,成员可能在磁盘上或数据库中。它们甚至可以动态计算,例如斐波那契数列,并且根本不会存储在任何地方。在这种情况下,size_type很容易大于size_t

于 2012-09-19T16:48:03.667 回答
5

我确信它被埋在某个地方的标准中,但我看到的关于size_type的最佳描述来自 SGI-STL 文档。正如我所说,我确信它在标准中,如果有人能指出它,一定要这样做。

根据 SGI,容器的 size_type 是:

可以表示容器距离类型的任何非负值的无符号整数类型

它没有声称必须是除此之外的任何东西。理论上,您可以定义一个使用 uint64_t、unsigned char 以及介于两者之间的任何内容的容器。它引用容器的 distance_type 是我觉得有趣的部分,因为......

distance_type:一个有符号整数类型,用于表示容器的两个迭代器之间的距离。此类型必须与迭代器的距离类型相同。

不过,这并不能真正回答问题,但有趣的是看看 size_type 和 size_t 如何不同(或可以)。关于您的问题,请参阅(并投票)@AnalogFile 的答案,因为我认为它是正确的。

于 2012-09-19T16:39:26.007 回答
3

从§18.2/6

该类型size_t是实现定义的无符号整数类型,其大小足以包含任何对象的字节大小。

因此,如果您可以分配一个大小不能由 a 表示的对象,那么size_t它会使实现不符合标准。

于 2012-09-19T16:48:07.533 回答
1

要添加到“标准”答案,还请注意stxxl项目,该项目应该能够使用磁盘存储(可能通过扩展,网络存储)处理 TB 的数据。例如,参见vector 的标题,size_type将(第 731行和第 742 行)定义为 uint64。

这是使用比内存容量更大的容器的具体示例,或者甚至系统的整数都可以处理。

于 2012-09-19T16:50:37.920 回答
0

不必要。

我假设 size_type 是指大多数 STL 容器中的 typedef?

如果是这样,那么仅仅因为 size_type 被添加到所有容器而不是仅仅使用 size_t 意味着 STL 保留将 size_type 设置为他们喜欢的任何类型的权利。(默认情况下,在所有实现中,我都知道 size_type 是 size_t 的 typedef)。

于 2012-09-20T07:34:13.070 回答