为什么 'char -> int' 是提升,而 'char -> Short' 是转换(而不是提升)?

LiD*_*ute 41 c++ type-conversion integer-promotion language-lawyer

我读过cppreference.com 的隐式转换

整数提升:
小整数类型(例如 char)的纯右值可以转换为较大整数类型(例如 int)的纯右值。

[...]

请注意,所有其他转化均不属于促销;例如,重载决策选择 char -> int (提升)而不是 char -> Short (转换)。

从charint的转换就是‘提升’;很清楚(从1字节到4字节)。
从 char -> Short 的转换是“不提升”;为什么?
我一直认为char是一个字节,short(short int)是两个字节。为什么它不被视为促销?这似乎与第一行相矛盾。难道‘小改大’就是升职吗?)


寻找一些答案后:

我们可以考虑“促销是转化的特例”吗?我们是否可以说“所有促销都是转化,但并非所有转化都是促销”?
或者我们应该将它们视为两个不同且独立的概念?

Jan*_*tke 40

历史动机:C

积分提升的思想可以追溯到标准之前的 C。当向可变参数函数 ( ...) 或没有原型的函数提供参数时,就会应用提升。即调用:

// function declaration with no prototype
void mystery();
// ...
char c = 'c';
mystery(c); // this promotes c to int

// in another .c file, someone could define
void mystery(int x) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

促销基本上是对该类型的“最小”升级,而转换可以是任何内容。您甚至可以认为该设计是基于 B 编程语言构建的结果,该语言甚至没有像 C 那样具有多种整数类型。

C++标准中的相关措辞

仅以下内容被视为整数提升:

如果纯右值不是转换后的位域,并且其整数类型不是boolchar8_tchar16_tchar32_t,或者wchar_t其整数转换等级小于 的等级,int则可以转换为该类型的纯右值int(如果int可以表示源类型的所有值) ; 否则,可以将源纯右值转换为 类型的纯右值unsigned int

- [转换舞会] p2

促销和转化之间的区别解释如下:

积分促销允许的转化不包括在积分转化集中。

- [转换积分] p4

正如您所看到的,这两个概念之间存在一些重叠,但任何同时也是促销的转换都不被视为转换。

标准转换是升级还是转换都会对重载解析产生影响:

标准转换序列按其等级排序:精确匹配是比促销更好的转换,促销是比转换更好的转换。[...]

- [over.ics.rank] p4

对过载解决的影响

正如您所指出的,char -> short是转换,char -> int是晋升。这有以下影响:

// A conversion sequence from char -> int is empty
// because char -> int is a promotion, and so it doesn't contribute
// to the conversion sequence.
void f(int);
// A conversion sequence from char -> short has length 1
// because char -> short is not a promotion.
void f(short);

int main() {
    // Not an ambiguous call; calls f(int), because the conversion sequence
    // for this call is shorter.
    // Note that in C, a character literal has type 'int', not 'char', so
    // it is more symmetrical to favor 'int' whenever possible.
    f('x');
}
Run Code Online (Sandbox Code Playgroud)

如果现在 C++ 是从头开始设计的,那么升级和转换的定义可能会有很大不同,但是,现状就是我们所拥有的,而且不太可能改变。经过这么多年,改变这种行为和措辞基本上是不可能的,因为有多少代码依赖于它。

与 C++ 中的许多设计决策一样,答案是:历史原因。

  • 大多数数学运算符的操作数也得到了提升。一个臭名昭著的例子是“unsigned Short x = 65535;” unsigned tmp = x*x;` 在具有 16 位 `short` 和 32 位 `int` 的机器上具有有符号溢出 UB,因为从 `short` 进行整数提升会产生 *signed* `int` = 65535。产生“0xfffe0001”的平方。它不会提升为“unsigned int”,因为有符号“int”涵盖了“uint16_t”的完整值范围。它不会保留为“unsigned Short”,因为它的转换等级小于“int”。 (5认同)
  • 唔。我自己也不确定,但是从小整型到“int”的“显式”转换(即使用强制转换)是否仍然被归类为“促销”?或者只有当编译器“自己做”时才会升级? (3认同)
  • @LiDaCute 好问题。我已经更新了答案来解释这一点。 (3认同)
  • @PeterCordes:此外,根据已发布的 C99 基本原理,将“无符号短”提升为“int”的决定是由于安静的环绕二进制补码实现(当时,并且可能预计将保留,当前的大多数实现)将处理“unsigned tmp=ushort1*ushort2;”的情况,其中数学乘积将以将结果包裹为负数的方式超过 INT_MAX,但然后将其返回以产生算术正确的结果。然而,像 gcc 这样的编译器不再这样做。 (3认同)

Eri*_*hil 16

tochar和toint都是转换。请注意文本\xe2\x80\x99s 在讨论促销后使用\xe2\x80\x9call other conversions\xe2\x80\x9d:促销是一种转换。charshort

\n

转换是一种操作,它采用一种形式的值并以另一种形式产生相同的值(尽可能接近):

\n
    \n
  • 获取一个char值并生成int具有相同值的值是一种转换。
  • \n
  • 获取一个int值并生成一个包含表示相同值的十进制数字的字符串是一种转换。
  • \n
  • 以磅为单位的重量并以公斤为单位产生相同的重量是一种转换。
  • \n
\n

在表达式的许多地方,C++ 自动将窄整数类型转换为更宽的整数类型。这样做主要是出于历史目的,而且还因为纯粹以窄类型进行算术可能很尴尬\xe2\x80\x94char算术很容易溢出,并且需要程序员显式插入强制转换int代码变得麻烦。

\n

这些特殊的转换称为 \xe2\x80\x9cpromotions。\xe2\x80\x9d 这个词的自然英语含义,前进到更高的位置,适合这些类型的转换:它们都从较窄的类型转换为较宽的类型(或至少一样宽)。除此之外,这个词被用来专门指代这些特殊的转换。积分促销的一个重要特点是其价值永远不会改变。(理想情况下,任何转换都不会更改值,但由于限制,这种情况确实会发生。一个示例是将float值 3\xc2\xbc 转换为int必须生成一个整数,因此生成 3 而不是 3\xc2\xbc。另一个示例是将int\xe2\x88\x923 的值转换为unsigned强制换行,因此会产生一个名义上非常不同的值,尽管它与输入值确实有特殊关系,并且在模算术中可以被视为相同的值。)

\n


小智 6

C++ 标准指定了一组整数提升规则,这些规则不仅仅基于类型的大小。char将 a 升级为 anint而不是 a的决定short基于该语言的历史设计以及跨不同平台一致行为的愿望。

根据 C++ 标准,定义整型提升以将小整型类型转换为特定的实现定义类型,通常是int. 由于int选择 为积分提升的目标类型,因此将 a 转换char为 ashort并不符合提升资格,即使short大于char。相反,它属于转换类别。

不同的实现和架构之间的具体规则可能有所不同,但这是您观察到的行为背后的一般推理。它是语言设计的一部分,以历史考虑和编译器实现的实际方面为指导,而不是基于类型大小的严格逻辑进展。

  • @AdrianMole:,“short”确实有一个需要 16 位的最小范围。但“char”也可以是 16 位。`sizeof` 返回 `CHAR_BITS` 的倍数。所以`sizeof(short)==1`仍然是允许的。 (6认同)