[英]Can a size_type ever be larger than std::size_t?
具有std::allocator
标准容器将其size_type
定义为std::size_t
。 但是,是否可以使用分配器来分配大小无法用size_t
表示的对象? 换句话说, size_type
是否可以大于size_t
?
是的,这在某些情况下可能有用。
假设您有一个程序希望访问的存储空间超过虚拟内存容量。 通过创建引用内存映射存储的分配器并在间接pointer
对象时根据需要进行映射,可以访问任意大量的内存。
这仍然符合18.2:6,因为size_t
定义为足以包含任何对象的大小,但17.6.3.5:2表28将size_type
定义为包含分配模型中最大对象的大小,不必是C ++内存模型中的实际对象。
请注意,表18中的17.6.3.5:2中的要求并不构成多个对象的分配应该产生数组的要求; 对于allocate(n)
,要求是:
为
n
类型的T
对象分配内存
并且对于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
。
sizeof
应该返回作为其参数的类型(或表达式类型)的大小。 在数组的情况下,它应该返回整个数组的大小。
这意味着:
不能有任何大于size_t
可以表示的结构或联合。
不能有任何大于size_t
可以表示的数组。
换句话说,如果某些东西适合你可以访问的最大连续内存块,那么它的大小必须适合size_t(非便携式,但易于直观地理解,这意味着在大多数系统中size_t
与void*
一样大) void*
并且可以“测量”整个虚拟地址空间。
编辑:下一句可能是错的。 见下文
因此答案是否有可能有一个分配器分配大小不能用size_t
表示的对象? 没有。
编辑(附录):
我一直在想它,而上面我实际上是错的。 我检查了标准,似乎可以设计一个完全自定义指针类型的完全自定义分配器,包括使用不同类型的指针,const指针,void指针和const void指针。 因此,分配器实际上可以具有大于size_t的size_type。
但要这样做,您需要实际定义完全自定义指针类型以及相应的allocator和allocator traits实例。
我之所以说可能的原因是,我仍然有点不清楚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_t
的size_type
吗?
@BenVoight和@ecatmur都建议使用后备存储是文件的用例。 但是,如果后备存储是仅用于内容的文件,并且您在内存中有一些引用该内容的内容(让我们称之为“句柄”),那么您实际上在做一个包含句柄的容器。 句柄将是某个类的实例,它将实际数据存储在文件中,并且只保留在内存中检索该数据所需的任何内容,但这与容器无关:容器将存储句柄,这些句柄位于内存中,我们仍处于“正常”地址空间,因此我的初始响应仍然有效。
然而,还有另一种情况。 你没有分配句柄,你实际上是在文件(或数据库)中存储东西,你的分配器(和相对特征)定义指针,const指针,void指针,const void指针等直接管理该后备存储的类型。 在这种情况下,当然,他们还需要定义size_type
(替换size_t
)和difference_type
(替换ptrdiff_t)来匹配。
当size_t
已经与最大实现提供的原始整数类型(如果没有,那么没有困难)一样大时,将size_type
(和difference_type
)定义为大于size_t
的直接困难与它们需要是integer types
的事实有关。
根据您对标准的解释方式,这可能是不可能的(因为根据标准integer types
是标准中定义的类型加上实现提供的extended integer types
)或者可能(如果您将其解释为可以提供extended integer type
你自己)只要你能编写一个行为与基本类型完全相同的类。 这在过去是不可能的(重载规则确实使原始类型总是与用户定义的类型区分开来),但我不是100%最新的C ++ 11,这可能(或可能不会改变)。
然而,也存在间接困难。 您不仅需要为size_type
提供合适的整数类型。 您还需要提供其余的分配器接口。
我一直在考虑它,我看到的一个问题是根据17.6.3.5实现*p
。 在那个*p
语法中, p
是由分配器特征键入的pointer
。 当然,我们可以编写一个类并定义一个operator*
(nullary方法版本,执行指针解除引用)。 有人可能认为这可以通过'分页'文件的相对部分轻松完成(如@ecatmur建议的那样)。 但是有一个问题: *p
必须是该对象的T&
。 因此,对象本身必须适合内存,更重要的是,因为您可以执行T &ref = *p
并无限期地保留该引用,一旦您在数据中分页,您将永远不会被允许再将其分页。 这意味着有效地可能无法正确实现这样的分配器,除非整个后备存储也可以加载到内存中。
这些是我早期的观察,似乎确实证实了我的第一印象,即真正的答案是否定的:没有实际可行的方法。
然而,正如你所看到的,事情要比直觉似乎更为复杂。 找到一个明确的答案可能需要相当长的时间(我可能会或可能不会继续进一步研究这个话题)。
目前我只想说: 似乎不可能 。 如果声明不完全基于直觉,则只能接受相反的陈述:发布代码并让人们争论如果你的代码完全符合17.6.3.5以及你的size_type
(即使size_t
大于size_t
也应大于size_t
)最大的原始整数类型)可以被认为是整数类型。
是的,不是。
正如@AnalogFile所解释的那样,没有分配的内存可以大于size_t
。 因此,它继承其容器size_type
从分配器不能有size_type
大于size_t
。
但是,您可以设计一个容器类型,表示不完全存储在可寻址内存中的集合。 例如,成员可以位于磁盘上或数据库中。 它们甚至可以动态计算,例如Fibonacci序列,并且从不存储在任何地方。 在这种情况下, size_type
可能很容易大于size_t
。
我确定它被埋在标准的某个地方,但我见过的最好的size_type描述来自SGI-STL文档。 正如我所说的那样,我确信它符合标准,如果有人能够指出它,那么一定要做到。
根据SGI,容器的size_type是:
无符号整数类型,可表示容器距离类型的任何非负值
除此之外,它没有任何声明。 理论上,您可以定义一个使用uint64_t,unsigned char以及其他任何内容的容器。 它引用容器的distance_type是我觉得有趣的部分,因为......
distance_type:一个带符号的整数类型,用于表示两个容器迭代器之间的距离。 此类型必须与迭代器的距离类型相同。
但是,这并没有真正回答这个问题,但是看看size_type和size_t的不同(或者可以)有趣。 关于你的问题,请参阅(和投票)@AnalogFile的回答,因为我认为这是正确的。
从§18.2/ 6开始
类型
size_t
是一个实现定义的无符号整数类型,它足够大,可以包含任何对象的字节大小。
因此,如果您可以分配一个大小无法用size_t
表示的对象,那么它将使实现不符合要求。
不必要。
我假设size_type是指大多数STL容器中的typedef?
如果是这样,那么只是因为size_type被添加到所有容器而不是仅使用size_t意味着STL保留了使size_type成为他们喜欢的任何类型的权利。 (默认情况下,在所有实现中,我都知道size_type是size_t的typedef)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.