当二元运算符两侧的符号性不同时,促销规则如何工作?

考虑以下程序:

// 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,但是在intunsigned 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的大小与shortlong大小不一样。 在许多现行的系统中, int长度相同, short则不同。 通过保值规则,这些可以导致两者之间的提升types不同。

价值保持规则的基本思想是,如果可以代表较小types的所有值,则它将被推广到较大的有符号types。 例如,一个16位的unsigned short可以被提升为一个32位的signed int ,因为unsigned short每个可能的值都可以表示为一个带signed int 。 types将被提升为一个无符号types当且仅当必要时保留较小types的值(例如,如果unsigned shortsigned int都是16位,那么signed int不能表示所有可能的unsigned shortunsigned 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的系统上, intshort是一样大小的,而且2)通常很难做到这一点。

它会select你把结果放到哪个types,或者至lesscout是在输出的时候遵循这个types。

我不太确定,但我认为C ++编译器为两者生成相同的算术代码,只是比较和输出关心符号。