标准库vector::size()
提供size_t
,无符号数。在其中一次CppCon会谈中,我听到有人(Chandler Carruth?)说这很不幸,而且应该使用签名整数。
背景是没有为有符号整数定义溢出,因此编译器有更多的余地。在一次演讲中,Carruth展示了bzip2中uint8_t
作为for
循环索引如何在x86上创建的机器指令多于int8_t
,因为它必须使用掩码和移位显式模拟溢出。< / p>
在我现在使用的代码中,有一些严格要求的大小。这些表示为size_t
。这看起来不错,因为这表明他们不能消极。另一方面,不需要定义的模运算,因此只要有符号整数足够大(我们去200),无符号整数就会有我们想要的算术错误的接口。
在代码中的某个时刻,存在从0到此大小的循环。然后减去循环索引并获取绝对值。
当我用更现代的GCC 7编译它时,它无法解决std::abs
的正确重载,因为size_t - size_t
显然具有暧昧的价值。我已将代码更改为在循环索引中使用int
:
for (int t1 = 0; t1 < Lt; t1++) {
for (int t2 = 0; t2 < Lt; t2++) {
现在abs(t1 - t2)
工作得很好。但比较t1 < Lt
会发出警告,因为它是有符号和无符号数字之间的比较。
什么是正确的方法?
static_cast<int>()
。static_cast<int>
。static_cast<int>
即可满足警告。答案 0 :(得分:2)
“在一次演讲中,Carruth展示了作为bzip2中for循环索引的uint8_t如何在x86上创建比int8_t更多的机器指令,因为它必须使用掩码和移位显式模拟溢出。”
好吧,如果您可以使用任何一种类型,则for-range必须限制为[0, 127]
。然后使用int
作为索引类型。 按照定义是基本数学运算的自然类型,通常可以很好地映射到CPU寄存器。
使用针对最小存储优化的类型将不会生成最快的数学,不会。这并不奇怪。根据这些有缺陷的设置,您无法得出有关已签名与未签名的结论。
“size_t
- size_t
提供不明确的值”
嗯,它没有,但确实使用了模运算。 size_t(1)-size_t(2)==size_t(-1)
,但size_t(-1)
是可能的最大值。这直接来自模块化数学的定义:x-1 < x
,除非x-1
包裹因为x==0
。 (或等效x+1>x
,除非x+1==0
)
因此,调用abs(size_t(x))
也毫无意义,因为每个size_t
值都是正数。将有符号整数与size_t
进行比较同样充满了意想不到的后果。明确的演员阵容很好,因为它们可以清楚地表明后果。
但是没有通用的解决方案来自动确定应该应用哪个演员表。如果可以发明机械规则,我们可以将该规则留给编译器。我们没有,因为我们做不到。作为程序员,您必须以数字方式考虑每个案例。