为什么QVector::size返回int?

24

std::vector::size() 返回一个无符号的 size_type,通常与 size_t 相同,在 64 位平台上它占据 8 字节。

相比之下,QVector::size() 返回一个带符号的 int,即使在 64 位平台上也通常只占据 4 字节,这意味着它只能到达 2^32 的一半。

为什么会这样呢?这似乎非常不合逻辑并且在技术上有限制,虽然你很少需要超过 2^32 个元素,但使用带符号的 int 将这个范围减半却没有明显的好处。也许这是为了避免编译器警告,对于懒得将 i 声明为 uint 而不是 int 的人来说,让所有容器返回一个毫无意义的 size 类型是更好的解决方案?难道原因只可能是这么愚蠢吗?


6
确实有可能会这么愚蠢。许多被广泛使用和尊重的库代码都包含了像这样脑残的风格错误。你会惊讶地发现,还有许多其他才华横溢的程序员甚至不知道像 size_t 这样的存在... - The Paramagnetic Croissant
这将会是有趣的工作,使用大于2GB的QByteArray,在我的领域中完全可以实现... - user3735658
2
@userXXX 完全同意。现在,搜索一些“便携式”的开源库/应用程序并阅读其源代码。当您发现它们充满了未定义的行为、非便携式结构、易于破坏的假设等时,会感到震惊。这是因为根据我的经验,大多数程序员都是实用主义者(委婉说法是“懒惰”),他们不按照语言标准编程,而是按照他们中间不称职的老师(!)和/或同事建议的方式进行编程。这很令人悲伤。非常非常悲伤。 - The Paramagnetic Croissant
5
太糟糕了,也很讽刺,我费心培养了使用无符号整数来遍历容器的良好习惯,现在却因为做得正确而受到编译器警告的惩罚。 - user3735658
1
也许这不是原因,但仍然哈哈大笑:http://qt-project.org/doc/qt-5/qvector.html#fill - BartoszKP
显示剩余4条评论
2个回答

19
自Qt 3以来,这个问题已经被讨论了好几次,QtCore的维护者表示在Qt 7之前(如果有的话)不会有任何改变。当时讨论进行时,我就想着总有人会在Stack Overflow上提出这个问题,可能还会在其他几个论坛和问答网站上提出。让我们试着揭开谜团。
总的来说,你需要明白这里没有更好或更差的选择,因为QVector并不是std::vector的替代品。后者不执行写时复制(COW),这也是有代价的。它基本上是为不同的用例而设计的。它主要用于Qt应用程序和框架本身,在早期主要用于QWidgets。
size_t也有它自己的问题,下面我会指出。我不会解释维护者的意思,只会直接引用Thiago的话来传达官方立场
因为有两个原因: 1)它被签署是因为我们需要在API的多个地方使用负值:indexOf()返回-1表示未找到值;许多“from”参数可以接受负值以指示从末尾计数。因此,即使我们使用64位整数,我们也需要其带符号版本。这是POSIX ssize_t或Qt qintptr。 这还避免了将无符号转换为有符号时出现的符号更改警告:
-1 + size_t_variable        => warning
size_t_variable - 1     => no warning

2) 这是为了避免转换警告或与使用大于int的整数相关的丑陋代码,因此简单地使用“int”。

io/qfilesystemiterator_unix.cpp

size_t maxPathName = ::pathconf(nativePath.constData(), _PC_NAME_MAX);
if (maxPathName == size_t(-1))

io/qfsfileengine.cpp

if (len < 0 || len != qint64(size_t(len))) {

io/qiodevice.cpp

qint64 QIODevice::bytesToWrite() const
{
    return qint64(0);
}

return readSoFar ? readSoFar : qint64(-1);

那是 Thiago 发来的一封电子邮件,然后 还有另一个 你可以找到一些详细的答案:

即使在今天,具有4 GB(甚至2 GB)以上核心内存的软件也是例外而不是规则。查看某些进程工具的内存大小时,请注意它们并不代表实际内存使用情况。无论如何,在这里我们谈论的是一个单一容器访问超过2 GB的内存。由于Qt容器的隐式共享和写时复制特性,这可能会非常低效。编写此类代码时需要非常小心,以避免触发COW,从而使内存使用量翻倍或更高。此外,Qt容器不能处理OOM情况,因此如果您接近内存限制,则使用Qt容器是错误的选择。我系统上最大的进程是qtcreator,它也是唯一一个在VSZ(4791 MB)中跨越4 GB标记的进程。您可以认为这表明需要64位容器,但您是错误的:Qt Creator没有任何需要64位大小的容器,它只需要64位指针。它没有使用4 GB的内存。那只是VSZ(映射内存)。目前对Creator可访问的总RAM仅为348.7 MB。而且它使用了超过4 GB的虚拟空间,因为它是一个64位应用程序。因果关系与您预期的相反。为了证明这一点,我检查了填充消耗的虚拟空间:800 MB。32位应用程序永远不会这样做,因为这占用了4 GB可寻址空间的19.5%。(填充是分配但没有被任何东西支持的虚拟空间;它只是存在,以便其他东西不会映射到这些页面上)

进一步了解Thiago的回答,看看这个:

就我个人而言,我非常高兴Qt集合大小是有符号的。对我来说,一个整数值可能在使用减法表达式时无符号(例如size_t),这似乎很疯狂。

整数无符号并不能保证涉及该整数的表达式永远不会为负数,只能保证结果将是绝对灾难。

另一方面,C和C ++标准定义了无符号溢出和下溢的行为。

有符号整数不会溢出或下溢。我的意思是,它们会因为类型和CPU寄存器有限的位数而发生,但标准规定它们不会。这意味着编译器将始终进行优化,假设您不会溢出或下溢它们。

示例:

for (int i = 1; i >= 1; ++i)

这段代码被优化为无限循环,因为有符号整数不会溢出。如果您将其更改为无符号,则编译器知道它可能会溢出并返回零。一些人不喜欢这样做:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475

还有一篇关于Qt容器的不错但过时的文章:http://marcmutz.wordpress.com/effective-qt/containers/ - nib

2

unsigned表示的是一种取模运算,在某些情况下,取模的值为2^n

带符号数是有界整数。

把无符号值作为“正整数”的近似值存在一个问题,即常见的值接近于该类型的边缘,而在该边缘处无符号值与普通整数的行为不同。

优点是无符号近似可以达到更高的正整数,并且溢出和下溢定义清楚(如果被看作Z模型,溢出和下溢是随机的)。

但实际上,使用ptrdiff_t比使用int更好。


我想到的另一个原因是:如果将无符号类型与有符号类型相加,结果类型为无符号。这可能会导致意外和难以追踪的行为。如果将 size() 添加到其他值中,结果为负数会怎样?除非以某种方式将其转换回有符号整数,否则可能会遇到麻烦。 - Taylor Brandstetter
@TaylorBrandstetter 是的,这是可能的;通常,使用有符号值更方便(轻率地说...)。但正如此答案所指出的那样,仍然会有 ptrdiff_t... - The Paramagnetic Croissant
@Taylor,你得到了一个很大的正数。这并不是“真实整数”的行为的好近似,但却是一个常见的情况。根据我的经验,有符号整数溢出最常见的来源实际上是无符号转有符号转换。 - Yakk - Adam Nevraumont

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接