为啥 Qt 对其容器类使用有符号整数类型?

Posted

技术标签:

【中文标题】为啥 Qt 对其容器类使用有符号整数类型?【英文标题】:Why does Qt use a signed integer type for its container classes?为什么 Qt 对其容器类使用有符号整数类型? 【发布时间】:2011-02-17 14:20:34 【问题描述】:

问题很明确。

我想知道为什么他们甚至认为这会很方便,因为显然负索引在与它们一起使用的容器中是不可用的(例如参见 QList's docs)。

我以为他们想允许这种疯狂的索引形式,但似乎不受支持?

它还会生成大量(正确的)编译器警告,关于强制转换和比较有符号/无符号类型(在 MSVC 上)。

出于某种原因,它似乎与 STL 的设计不兼容...

【问题讨论】:

【参考方案1】:

虽然我对 Chris 的推理深表同情,但在这里我不同意(至少在某种程度上,我在扮演魔鬼的拥护者)。对大小使用无符号类型没有任何问题,而且在某些情况下甚至是有益的。

Chris 对有符号大小类型的理由是它们自然用作数组索引,您可能希望对数组索引进行算术运算,而该算术运算可能会创建负的临时值。

这很好,无符号算术这样做不会带来任何问题,只要您确保在进行比较时正确解释您的值。因为无符号整数的溢出行为是完全指定的,所以临时溢出到负范围(或巨大的正数)不会引入任何错误,只要它们在执行比较之前被纠正。

有时,溢出行为甚至是可取的,因为无符号算术的溢出行为使得某些范围检查可以表达为单个比较,否则需要两次比较。如果我想检查x 是否在[a,b] 范围内并且所有值都是无符号的,我可以简单地做:

if (x - a < b - a) 

这不适用于有符号变量;这种范围检查在大小和数组偏移中很常见。

我之前提到过一个好处是溢出算法已经定义了结果。如果您的索引算术溢出有符号类型,则行为是实现定义的;没有办法让你的程序可移植。使用无符号类型,这个问题就消失了。诚然,这仅适用于 巨大 偏移量,但对于某些用途来说这是一个问题。

基本上,对无符号类型的反对意见经常被夸大了。真正的问题是大多数程序员并没有真正考虑他们编写的代码的确切语义,对于 small 整数值,有符号类型的行为更接近于他们的直觉。但是,数据大小增长得非常快。当我们处理缓冲区或数据库时,我们经常超出“小”范围,并且正确处理有符号溢出比无符号溢出问题要严重得多。解决方案不是“不要使用无符号类型”,而是“仔细考虑您正在编写的代码,并确保您理解它”。

【讨论】:

我真的很喜欢无符号索引的惰性下溢检查样式。 @斯蒂芬。你的意思是(x - a @StephenKennedy 是的。【参考方案2】:

因为实际上,您通常希望对索引执行算术运算,这意味着您可能希望创建负数的临时变量。 当底层索引类型是无符号的时,这显然很痛苦。

使用无符号数的唯一合适时间是模数运算。 使用“unsgined”作为某种契约说明符“[0...范围内的数字”只是笨拙的,而且太粗糙而无用。

考虑:我应该使用什么类型来表示数字应该是介于 1 和 10 之间的正整数?为什么 0...2^x 是一个更特殊的范围?

【讨论】:

"only" 是一个相当强的说明符。 我同意更一般的观点,即无符号类型在其作为文档的常用角色中被高估了。 @Chris:但是环绕可能很讨厌,因为这意味着您的数组索引变为 0、1、2、32766、32767、-32768、-32767,...,您不能再如果x 的索引小于y 的索引,则说x 在数组中的y 之前。也就是说,混合无符号和有符号数量往往会导致错误,这些错误与人们必须记住在比较它们之前将索引转换为无符号类型所导致的错误一样糟糕,并且更有可能在实践中实际发生。 所以我们背负着一个愚蠢而危险的 size_t 定义,以确保使用 char 数组来索引内存的极端情况不会在剩余的极少数情况下遭受不良的未定义行为非二进制补码 CPU 模型。 @Steve 使用任何非 char 类型意味着,在有符号数量将环绕的点上,无符号数量将索引超过可用内存(并环绕)。无论索引是有符号还是无符号,最终结果都是相同的:某个“随机”内存位置被索引。

以上是关于为啥 Qt 对其容器类使用有符号整数类型?的主要内容,如果未能解决你的问题,请参考以下文章

为啥容器类型没有类型类?

Qt容器类之一:Qt的容器类介绍

非模板类的模板构造函数出现问题[重复]

非 QObject 内的 Qt 容器

Qt容器类的对象模型及应用(线性结构篇)(好多图,比较清楚)

Qt之容器类