使用std::allocator
的标准容器将其size_type
定义为std::size_t
。然而,是否可能有一个分配器来分配大小不能用size_t
表示的对象?换句话说,size_type
可以比size_t
更大吗?
使用std::allocator
的标准容器将其size_type
定义为std::size_t
。然而,是否可能有一个分配器来分配大小不能用size_t
表示的对象?换句话说,size_type
可以比size_t
更大吗?
是的,在某些情况下这可能会很有用。
假设您有一个程序希望访问比虚拟内存中可容纳的更多的存储空间。通过创建引用内存映射存储的分配器,并在间接指针
对象时根据需要进行映射,您可以访问任意大量的内存。
这仍然符合18.2:6,因为size_t
定义为足够大以包含任何对象的大小,但17.6.3.5:2表28定义size_type
包含分配模型中最大对象的大小,该对象不必是C++内存模型中的实际对象。
请注意,17.6.3.5:2表28中的要求并不构成多个对象分配应导致数组的要求;对于allocate(n)
,要求是:
为类型
T
的n
个对象分配内存
对于deallocate
,断言是:
在此调用之前,指向
p
的区域中的所有n
个T
对象都将被销毁。
请注意区域而不是数组。 另一个要点是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
相同。
在另一个模型中可表达的模型中包含不能在外部模型中表示的对象是完全合法的;例如,数学逻辑中的非标准模型。
size_t
是应用sizeof
操作得到的无符号整数类型。
sizeof
应返回其参数所表示的类型(或表达式类型)的大小。对于数组,它应该返回整个数组的大小。
这意味着:
不能有任何大于 size_t
能够表示的结构体或联合体。
不能有任何大于 size_t
能够表示的数组。
换句话说,如果某些东西适合你可以访问的最大连续内存块,那么其大小必须适合 size_t
(以不具可移植性但易于理解的直观术语来说,这意味着在大多数系统上,size_t
和 void*
一样大,并且可以"测量"您的虚拟地址空间的全部内容)。
编辑:下面的句子可能是错误的。请参见以下内容。
因此,回答“是否可能有一个分配器,分配对象的大小无法由 size_t
表示?”是否定的。
编辑(补充):
我一直在考虑这个问题,上面的回答可能是错误的。我检查了标准,似乎可以设计一个完全自定义的分配器,使用完全自定义的指针类型,包括使用不同类型的指针、const 指针、void 指针和const void指针。因此,实际上可以有一个 size_type 大于 size_t 的分配器。
但要这样做,您需要定义完全自定义的指针类型以及相应的分配器和分配器特性实例。
我说“可能”是因为我还不太清楚 size_type
是否需要跨越分配器模型中单个对象的大小或多个对象(即数组)的大小。我将需要调查这个细节(但现在不行,这里是晚餐时间 :))
编辑2(新补充):
@larsmans 我认为你可能需要决定接受什么。这个问题似乎比人们直觉上意识到的要复杂一些。我再次编辑答案,因为我的想法明显比评论更详尽(无论是内容还是大小)。
重新编辑(正如评论中指出的那样,下面的两段话是不正确的):
首先,size_type
只是一个名称。当然,您可以定义一个容器并添加一个 size_type
,并赋予其任何您希望的含义。您的 size_type
可以是浮点数,也可以是字符串等。
size_type
仅定义在容器中以方便访问,实际上应与该容器的分配器的size_type
相同(而分配器的size_type
应为该分配器的allocator_traits
的size_type
)。因此,我们从现在开始假设容器的size_type
,即使是您自己定义的容器,也遵循相同的约定。@BenVoight以“As @AnalogFile解释,没有分配的内存可以大于size_t
。因此,继承其size_type
的容器不能具有大于size_t
的size_type
。”开头回答了这个问题。实际上,我们现在规定如果容器有一个size_type
,那么它来自分配器(他说继承,但这当然不是类继承的常见意义)。size_type
来自分配器,它是否一定受限于size_t
。问题实际上是:分配器(和相应的traits)是否可以定义一个大于size_t
的size_type
?size_type
(替换size_t
)和difference_type
(替换ptrdiff_t)以匹配。size_type
(和difference_type
)大于size_t
的直接困难是当size_t
已经作为最大的实现提供的原始整数类型时(如果不是,那么就没有困难),它们需要是整数类型
。根据标准的解释方式,这可能是不可能的(因为根据标准,整数类型是指标准中定义的类型加上由实现提供的扩展整数类型),或者是可能的(如果您将其解释为可以自己提供扩展整数类型),只要您能编写一个像原始类型一样行为的类。在旧时代是不可能的(重载规则使原始类型始终可与用户定义类型区分),但我对C++11不是100%熟悉,这可能已经改变了。sizeof(size_t)
是8,sizeof(long)
、sizeof(long long)
和sizeof(void*)
也是8。实际上,任何64位系统都会有sizeof(size_t)
为8。而且很少有系统的long long
超过64位(或者超过128位)。如果你的size_t
是32位,那么你就是在一个32位的系统上(说实话,这感觉有点过时,因为英特尔最后一个非64位处理器发布已经有大约8年了)。 - Analog File是和不是。
@AnalogFile解释说,没有分配的内存可以比size_t
更大。因此,从allocator继承其size_type
的容器不能具有比size_t
更大的size_type
。
然而,您可以设计一种容器类型,它表示一个不完全存储在可寻址内存中的集合。例如,成员可能存储在磁盘或数据库中。它们甚至可以动态计算,例如斐波那契数列,并且根本不存储任何地方。在这种情况下,size_type
可以很容易地比size_t
更大。
我确信它在标准中被隐藏了起来,但我见过的最好的size_type描述来自SGI-STL文档。正如我所说的,我相信它在标准中存在,如果有人能指出来,那就请指出来。
根据SGI的说法,容器的size_type是:
一个无符号整数类型,可以表示容器的距离类型的任何非负值
它并没有声称除此之外还必须是什么。理论上,您可以定义一个使用uint64_t、unsigned char和介于两者之间的任何其他内容的容器。它引用容器的distance_type这一部分让我觉得很有趣,因为...
distance_type:用于表示容器迭代器之间距离的有符号整数类型。此类型必须与迭代器的距离类型相同。
虽然这并没有真正回答问题,但看到size_type和size_t之间的区别(或可能性)还是很有趣的。关于您的问题,请参见(并点赞)@AnalogFile的答案,因为我认为它是正确的。
size_t
,但是使用64位文件系统的磁盘分配器,这意味着distance_type
和size_type
将是64位偏移量。 - Fred Foo来自§18.2/6
size_t
类型是一个实现定义的无符号整数类型,它足够大以包含任何对象的字节大小。
因此,如果您可以分配一个大小无法由size_t
表示的对象,则会使实现不符合规范。
N
的对象,以便其自己的size()
函数返回N
。想想std::list
。因此,容器的大小类型与用于单个对象大小的类型之间没有固有的关系,除了实际上它们通常都是内存空间的大小。 - Steve Jessopstd::list
可能会要求其 allocator 分配与所包含对象大小相同的块。也许我的答案也没有很清楚地表达这一点,但我是在谈论分配器对单个分配请求的大小限制。 - PraetorianSIZE_MAX
大的对象。 我忘记了我们正在讨论哪种size_type
。但是正如ecatmur所解释的那样,当分配器分配“足够多的N个东西”的内存时,它们分配的内存不一定是对象,尽管每个N个东西都是对象。 - Steve Jessopstd::list
在使用C++11 ABI时实际上使用size_t
作为节点计数(因此存在错误的max_size()
)来发现了这个问题... - FrankHBsize_type
(第731行和第742行)的定义为uint64。不一定。
我猜你说的 size_type 是指大多数 STL 容器内部的 typedef?
如果是这样,那么仅仅因为在所有容器中都添加了 size_type 而不是只使用 size_t,这意味着 STL 保留了将 size_type 设为任何他们喜欢的类型的权利。(默认情况下,在我所知道的所有实现中,size_type 都是 size_t 的 typedef)。
allocate
的措辞为“为n T
数组分配内存并创建这样的对象,但不构造数组元素”。 - FrankHB