在 类型转换与 static_cast 简介 中,我们首次介绍了类型转换。现回顾该课要点:
- 将数据由一种类型转换为另一种类型的过程称为“类型转换”。
- 隐式类型转换(implicit type conversion)由编译器自动完成:当某处需要类型 A,却提供了类型 B 时,编译器自动执行转换。
- 显式类型转换(explicit type conversion)通过强制转换运算符(如
static_cast
)主动请求。 - 转换过程并不修改原数据,而是以其为输入,生成目标类型的结果。
- 当把某值转换为另一类型时,转换过程会产生一个临时对象,其类型为目标类型,并保存转换结果。
本章前半部分将深入探讨类型转换机制。本课聚焦隐式转换,后续 —— 显式类型转换(强制转换)与 static_cast 将讨论显式转换。由于类型转换无处不在,理解其底层机理十分重要,亦有助于掌握重载函数的工作方式。
作者注
本章仅讨论“值到值”的类型转换。涉及指针、引用、继承等主题的其他转换,将在引入必备知识后另行说明。
为何需要转换
对象的值以一串二进制位存储,其数据类型告知编译器如何解释这些位。不同数据类型可能对“同一数值”采用不同表示。例如,整数 3 的二进制可能为0000 0000 0000 0000 0000 0000 0000 0011
,
而浮点数 3.0 的二进制可能为0100 0000 0100 0000 0000 0000 0000 0000
。
若执行:
float f{ 3 }; // 用 int 3 初始化浮点变量
编译器不能简单地把表示 int 值 3 的位模式直接复制到 float 变量 f 的内存中。若如此,当按 float 解释这些位时,结果未知。
附注
以下程序把 int 值 3 的位模式当作 float 打印:
#include <iostream>
#include <cstring>
int main()
{
int n { 3 }; // int 值 3
float f {}; // 浮点变量
std::memcpy(&f, &n, sizeof(float)); // 把 n 的位复制到 f
std::cout << f << '\n'; // 以 float 打印
return 0;
}
输出:
4.2039e-45
正确的做法是:将整数值 3 转换为等价的浮点值 3.0,再将其位模式存入变量 f。
隐式类型转换何时发生
隐式类型转换(亦称自动转换或强制)由编译器在需要某类型却提供另一类型的表达式时自动执行。C++ 中绝大多数类型转换为隐式转换,例如:
- 用不同类型值初始化或赋值:
double d{ 3 }; // int 3 → double
d = 6; // int 6 → double
- 函数返回类型与声明不符:
float doSomething()
{
return 3.0; // double 3.0 → float
}
- 二元运算符操作数类型不同:
double division{ 4.0 / 3 }; // int 3 → double
- 非布尔值用于条件判断:
if (5) // int 5 → bool
{
}
- 实参类型与形参类型不同:
void doSomething(long l) {}
doSomething(3); // int 3 → long
编译器如何知晓转换规则?
标准转换
C++ 标准定义了一套核心转换规则,称为“标准转换”(standard conversions),规定了基本类型(及数组、引用、指针、枚举等复合类型)如何在同组类型间互转。
截至 C++23,共 14 种标准转换,可分为 5 大类:
类别 | 含义说明 | 相关课程链接 |
---|---|---|
数值提升 | 小整数类型提升为 int/unsigned int;float 提升为 double | 10.2 — 浮点与整数提升 |
数值转换 | 除提升外的其他整数与浮点转换 | 10.3 — 数值转换 |
限定符转换 | 增加或移除 const/volatile | |
值类别转换 | 改变表达式的值类别 | 12.2 — 值类别(左值与右值) |
指针转换 | nullptr 到指针、指针到另一指针等 |
例如,int → float 属于“数值转换”,编译器只需应用相应规则即可。
进阶读者
以下为完整标准转换列表(略,原文已详列,此处从略)。
类型转换可能失败
当请求转换(无论隐式或显式)时,编译器判断能否从源类型转到目标类型。若存在合法转换,则生成目标类型的新值;否则编译报错。失败原因多样:
- 编译器不知如何转换。例如:
int main()
{
int x { "14" }; // 无 string literal → int 的标准转换
return 0;
}
GCC 报错:prog.cc:3:13: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
。
- 某些场景禁止某类转换。例如:
int x { 3.5 }; // 列表初始化禁止导致数据丢失的窄化转换
即使编译器知晓 double → int 的规则,但窄化转换在列表初始化中被禁止。
- 存在多个可行转换,编译器无法选出最优者(将在 —— 函数重载决议与二义性匹配 中讨论)。
完整转换规则冗长且复杂,但日常开发中“转换通常能正常工作”。后续课程将详述标准转换中最重要的部分;若遇罕见情形需更精细规则,可查阅技术参考文档关于隐式转换的完整说明。
让我们继续!