章节目录

隐式类型转换

本节阅读量:

类型转换简介

对象的值存储为二进制序列,而对象的数据类型会告诉编译器如何将这些二进制位解释为有意义的值。不同的数据类型可以用不同的方式表示“相同”的数字。例如,整数值3可以存储为二进制0000 0000 0000 0000 0000 0000 0000 0011,而浮点值3.0可以存储为二进制0100 0000 0100 0000 0000 0000 0000 0000。

那么当我们这样做时会发生什么呢?

1
float f{ 3 }; // 使用整数 3 去初始化浮点数

在这种情况下,编译器不能只把表示int值3的二进制数据复制到为浮点变量f分配的内存中。相反,它需要先将整数值3转换为等效的浮点值3.0,然后再将其存储到f的内存中。

从其他类型的值产生某种类型的新值的过程称为转换(conversion)。

类型转换可以通过两种方式触发:隐式触发(根据编译器需要)或显式触发(由程序员显式请求)。本节介绍隐式类型转换。


隐式类型转换

当需要某种类型的数据,但实际提供的是其他类型的数据时,编译器会自动执行隐式类型转换(Implicit type conversion,也称为自动类型转换 automatic type conversion)。C++中的绝大多数类型转换都是隐式类型转换。例如,以下所有情况都会发生隐式类型转换:

  1. 使用其他数据类型的值初始化(或为其赋值)变量时:
1
2
double d{ 3 }; // int 3 隐式转换为 double 类型
d = 6; // int 6 隐式转换为 double 类型
  1. 当返回值的类型与函数声明的返回类型不同时:
1
2
3
4
float doSomething()
{
    return 3.0; // double 3.0 隐式转换为 float
}
  1. 将某些二元运算符与不同类型的操作数一起使用时:
1
double division{ 4.0 / 3 }; // int 3 隐式转换为 double 类型
  1. 在if语句中使用非布尔值时:
1
2
3
if (5) // int 5 隐式转换为 bool 类型
{
}
  1. 当传递给函数的值与函数参数的类型不同时:
1
2
3
4
5
void doSomething(long l)
{
}

doSomething(3); // int 3 隐式转换为 long 类型

类型转换时会发生什么

当类型转换发生时(无论是隐式还是显式),编译器将确定它是否可以将值从当前类型转换为所需的类型。如果可以找到有效的转换,则编译器将生成所需类型的新值。请注意,类型转换不会更改要转换的值或对象的值或类型。

如果编译器找不到可接受的转换,则编译将失败,并产生编译错误。类型转换可能因各种原因失败。例如,编译器可能不知道如何在原始类型和目标类型之间转换值。也可能是语句本身不允许某些类型的转换。例如:

1
int x { 3.5 }; // 大括号初始化,不允许有精度丢失的类型转换

即使编译器知道如何将double值转换为int值,但在使用大括号初始化时,也不允许进行这种转换。

在某些情况下,编译器可能无法确定几个可能的类型转换中的哪一个是最好的。我们将在后续函数重载解析和匹配中看到这样的例子。

那么编译器实际上如何确定它是否可以将值从一种类型转换为另一种类型呢?


标准转换

C++语言标准定义了如何将不同的基本类型(在某些情况下,复合类型)转换为其他类型。这些转换规则称为标准转换。

标准转换可大致分为4类,每个类别涵盖不同类型的转换:

  1. 数值提升
  2. 数值转换
  3. 算术转换
  4. 其他转换(包括各种指针和引用转换)

当需要类型转换时,编译器将查看是否存在可用于将值转换为所需类型的标准转换。编译器可以在转换过程中应用零个、一个或多个标准转换。

描述类型转换如何工作的完整规则集既冗长又复杂,而且在大多数情况下,类型转换“只是默默地生效”。在后续课程中,我们将介绍关于类型转换最需要掌握的内容。如果某些不常见的情况需要更精细的细节,可以参考隐式转换的技术参考文档,其中详细介绍了完整规则。

让我们开始吧!


9.6 第9章总结

上一节

10.1 数值提升

下一节