当二元运算符两侧的符号性不同时,促销规则如何工作?
考虑以下程序:
// http://ideone.com/4I0dT #include <limits> #include <iostream> int main() { int max = std::numeric_limits<int>::max(); unsigned int one = 1; unsigned int result = max + one; std::cout << result; }
和
// http://ideone.com/UBuFZ #include <limits> #include <iostream> int main() { unsigned int us = 42; int neg = -43; int result = us + neg; std::cout << result; }
+运算符如何“知道”返回哪个types? 一般的规则是把所有的参数转换成最宽的types,但是在int
和unsigned int
之间没有明确的“胜利者”。 在第一种情况下,由于我得到了2147483648
的结果,所以必须selectunsigned int
作为operator+
的结果。 在第二种情况下,它必须selectint
,因为我得到-1
的结果。 然而,在一般情况下,我不认为这是可以确定的。 这是不明确的行为,我看到还是别的?
这在第5/9号中明确地概述:
许多期望算术或枚举types的操作数的二元运算符以相似的方式导致转换和产生结果types。 目的是产生一个共同的types,这也是结果的types。 这种模式被称为通常的算术转换 ,其定义如下:
- 如果任一操作数的types是
long double
,则另一个应转换为long double
。- 否则,如果其中一个操作数是
double
,另一个将被转换为double
。- 否则,如果任一操作数是
float
,则另一个操作数应转换为float
。- 否则,必须对两个操作数进行积分升级。
- 然后,如果任一操作数是
unsigned long
,另一个将被转换为unsigned long
。- 否则,如果一个操作数是一个
long int
而另一个是unsigned int
,那么如果一个long int
可以表示一个unsigned int
所有值,那么unsigned int
将被转换为一个long int
; 否则这两个操作数都将被转换为unsigned long int
。- 否则,如果其中一个操作数很
long
,另一个应转换为long
。- 否则,如果任一操作数是
unsigned
,则另一个应转换为unsigned
。[ 注意 :否则,唯一剩下的情况是两个操作数都是
int
]
在这两种情况下, operator+
的结果都是unsigned
。 因此,第二种情况是有效的:
int result = static_cast<int>(us + static_cast<unsigned>(neg));
因为在这种情况下, us + neg
值不能用int
表示, result
的值是实现定义的 – §4.7/ 3:
如果目标types是有符号的,那么如果目标types可以用目标types(和位域宽度)表示,则该值不变。 否则,该值是实现定义的。
在C标准化之前,编译器之间存在差异 – 一些遵循“保值”规则,另一些遵循“保持签名”规则。 保持符号意味着如果任一操作数是无符号的,结果是无符号的。 这很简单,但有时会给出相当惊人的结果(尤其是当一个负数被转换为无符号时)。
C在相当复杂的“保值”规则上进行标准化。 在保值规则下,升级可以取决于types的实际范围,因此您可以在不同的编译器上获得不同的结果。 例如,在大多数MS-DOS编译器中, int
的大小与short
和long
大小不一样。 在许多现行的系统中, int
长度相同, short
则不同。 通过保值规则,这些可以导致两者之间的提升types不同。
价值保持规则的基本思想是,如果可以代表较小types的所有值,则它将被推广到较大的有符号types。 例如,一个16位的unsigned short
可以被提升为一个32位的signed int
,因为unsigned short
每个可能的值都可以表示为一个带signed int
。 types将被提升为一个无符号types当且仅当必要时保留较小types的值(例如,如果unsigned short
和signed int
都是16位,那么signed int
不能表示所有可能的unsigned short
值unsigned short
,所以一个unsigned short
将被提升为unsigned int
)。
当你分配结果时,结果将被转换为目标types,因此大部分的结果几乎没有什么区别 – 至less在大多数情况下,它只是将结果复制到结果中,由您决定是否将其解释为有符号或无符号。
当你不分配比较的结果时,事情会变得相当丑陋。 例如:
unsigned int a = 5; signed int b = -5; if (a > b) printf("Of course"); else printf("What!");
在符号保持规则下, b
将被提升为unsigned,并且在这个过程中等于UINT_MAX - 4
,所以“What!” if
将采取的腿。 有了保值规则,你可以设法产生一些奇怪的结果,有点像这样,但是1)主要是在类似于DOS的系统上, int
和short
是一样大小的,而且2)通常很难做到这一点。
它会select你把结果放到哪个types,或者至lesscout是在输出的时候遵循这个types。
我不太确定,但我认为C ++编译器为两者生成相同的算术代码,只是比较和输出关心符号。