章节目录

算术转换

本节阅读量:

在前面运算符优先级和结合性中,我们讨论了如何根据表达式的运算符的优先级和联合性来计算表达式。

考虑以下表达式:

1
int x { 2 + 3 };

当调用二元运算符+时,它被赋予两个操作数,都是int类型的。由于这两个操作数都是相同的类型,因此将使用该类型执行计算并返回结果。因此,2+3将计算为int值5。

但当二元运算符的操作数是不同类型时会发生什么呢?

1
??? y { 2 + 3.5 };

在这种情况下,操作符+被赋予一个int类型的操作数和另一个double类型的操作数。操作符的结果应该作为int、double或可能的其他类型返回吗?定义变量时,我们可以选择它的类型。在其他情况下,例如,当使用std::cout«时,计算结果的类型会更改输出的行为。

在C++中,某些运算符要求其操作数具有相同的类型。如果使用不同类型的操作数调用这些操作符,则使用一组称为常用算术转换的规则将其中一个或两个操作数隐式转换为匹配类型。


需要相同类型操作数的运算符

以下运算符要求其操作数的类型相同:

  1. 二元算术运算符:+, -, *, /, %
  2. 二元关系运算符:<, >, <=, >=, ==, !=
  3. 二进制逐位算术运算符:&, ^, |
  4. 条件运算符 ? :(不包括条件表达式,条件表达式应为bool类型)

常用的算术转换规则

通常的算术转换规则有点复杂,因此我们将进行一些简化。编译器具有如下所示的类型排名列表:

  1. long double(最高级别)
  2. double
  3. float
  4. long long
  5. long
  6. int(最低级别)

应用以下规则来查找匹配类型:

  • 如果一个操作数是整型,另一个是浮点型,则整型操作数转换为浮点操作数的类型(不进行数值提升)。

否则,任何整数操作数都将进行数值提升。

  • 如果一个操作数有符号,另一个无符号,则应用特殊规则(见下文)
  • 否则,较低级别类型的操作数将转换为较高级别的类型。

一些例子

二元运算符+是要求其操作数具有相同类型的运算符之一。

在下面的示例中,我们将使用typeid操作符(包含在头文件中)来显示表达式的结果类型。

首先,让我们添加一个int和一个double:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <iostream>
#include <typeinfo> // for typeid()

int main()
{
    int i{ 2 };
    double d{ 3.5 };
    std::cout << typeid(i + d).name() << ' ' << i + d << '\n'; // 显示 i + d 结果的类型

    return 0;
}

在这种情况下,double具有最高优先级,因此较低优先级的操作数(int类型)被类型转换为double值2.0。然后将double值2.0和3.5相加,以产生double结果5.5。

在作者的机器上,此命令打印:

1
double 5.5

请注意,编译器可能会显示稍微不同的内容,因为typeid.name()的输出由编译器决定。

现在,让我们添加两个short类型的值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <iostream>
#include <typeinfo> // for typeid()

int main()
{
    short a{ 4 };
    short b{ 5 };
    std::cout << typeid(a + b).name() << ' ' << a + b << '\n'; // 显示 a + b 结果的类型

    return 0;
}

由于两个操作数都不出现在优先级列表中,因此这两个操作数都进行整型提升为int类型。两个int相加的结果是int,正如您所期望的那样:

1
int 9

有符号/无符号的问题

当混合有符号值和无符号值时,这种优先级层次结构和转换规则可能会导致一些问题。例如,查看以下代码:

1
2
3
4
5
6
7
8
9
#include <iostream>
#include <typeinfo> // for typeid()

int main()
{
    std::cout << typeid(5u-10).name() << ' ' << 5u - 10 << '\n'; // 5u 意味着无符号值 5

    return 0;
}

您可能希望表达式5u-10的计算结果为-5,因为5-10=-5。但实际结果是:

1
unsigned int 4294967291

由于转换规则,int操作数被转换为无符号int。由于值-5超出了无符号int的范围,因此得到了一个意外的结果。

下面是另一个显示违反直觉的结果的示例:

1
2
3
4
5
6
7
8
#include <iostream>

int main()
{
    std::cout << std::boolalpha << (-3 < 5u) << '\n';

    return 0;
}

虽然我们很清楚5大于-3,但当这个表达式求值时,-3被转换为大于5的大型无符号整数。因此,上面打印的是false结果,而不是true的预期结果。

这是避免无符号整数的主要原因之一,当您在算术表达式中将它们与有符号整数混合时,您可能会遇到意外结果。编译器甚至可能不会发出警告。


10.3 窄化转换、列表初始化和constexpr初始化

上一节

10.5 显式类型转换和static_cast

下一节