static_cast
转换
来自cppreference.com
使用隐式和用户定义转换的组合来进行类型之间的转换。
语法
static_cast< 目标类型 >( 表达式 )
|
|||||||||
返回目标类型 类型的值。
解释
只有下列转换在不移除常量性(或易变性)的场合才能用 static_cast 执行。
1) 如果表达式 是 “cv1
Base
” 类型左值且目标类型 是“到 cv2 Derived
的引用”,那么在满足以下所有条件时结果指代表达式 的外围 Derived
类型对象:
-
Derived
是完整类类型。 -
Base
是Derived
的基类 - cv1 是不多于 cv2 的 cv 限定。
如果满足以下任意条件,那么程序非良构:
如果表达式 实际上不是某个
Derived
类型对象的基类子对象,那么行为未定义。
struct B {}; struct D : B { B b }; D d; B& br1 = d; B& br2 = d.b; static_cast<D&>(br1); // OK,该左值指代原来的 d 对象 static_cast<D&>(br2); // 未定义行为:子对象 b 不是基类子对象
2) 如果目标类型 是“到
Derived 的右值引用”且表达式 是“(可有 cv 限定的)Base ” 类型亡值,其中 Base 是 Derived 的基类,那么这种转换的结果和约束与 “Base 左值到 Derived 引用”转换的相同。3) 如果目标类型 是右值引用类型且被引用类型引用兼容表达式 的类型,那么 static_cast 将泛左值、类纯右值或数组纯右值 (C++17 前)任何左值 (C++17 起)表达式 的值转换为与该表达式指代相同对象,或指代它的基类子对象(取决于目标类型)的亡值。[1]
如果目标类型 是表达式 的不可访问或有歧义的基类,那么程序非良构。
如果表达式 是位域左值,那么它会首先被转换成底层类型的纯右值。
|
(C++11 起) |
5) 否则,
如果对于某个虚设变量 temp 存在良构声明 目标类型 temp(表达式 );,那么表达式 可以显式转换到目标类型。 此类显式转换的效果与在进行该声明和初始化后将 temp 作为转换结果相同。当且仅当该初始化将表达式 作为左值 (C++11 前)泛左值 (C++11 起)使用时,转换才会将它作为作为左值 (C++11 前)泛左值 (C++11 起)使用。 |
(C++17 前) | ||
如果满足以下任意条件,那么表达式 可以显式转换到目标类型:
此类显式转换定义如下:
|
(C++17 起) |
6) 否则,如果从表达式 到目标类型 的转换是某个标准转换序列的逆转,并且该转换序列不含以下任何转换,那么可以通过 static_cast 进行该逆转:
(C++17 起) |
如果程序使用 static_cast 进行了某个非法标准转换序列的逆转,那么它非良构。
7) 否则,对表达式 应用左值到右值、数组到指针和函数到指针转换。在这些转换后,只有以下转换能通过 static_cast 进行:
a) 有作用域枚举类型的值可以转换到整数或浮点类型。
|
(C++11 起) |
b) 整数或枚举类型的值可以转换到任意完整的枚举类型。
d) 浮点类型的纯右值可以转换到其他任意浮点类型。
|
(C++23 起) |
e) “指向 cv1
Base
的指针”类型的右值 (C++11 前)纯右值 (C++11 起)在满足以下所有条件时可以显式转换到“指向 cv2 Derived
的指针”:
-
Derived
是完整类类型。 -
Base
是Derived
的基类 - cv1 是不多于 cv2 的 cv 限定。
如果满足以下任意条件,那么程序非良构:
如果表达式 既不是空指针值,实际上也不指向某个
Derived
类型对象的基类子对象,那么行为未定义。f) “指向
Derived
的 cv1 T
类型成员的指针”类型的右值 (C++11 前)纯右值 (C++11 起)在满足以下所有条件时可以显式转换到“指向 Base
的 cv2 T
类型成员的指针”:
-
Derived
是完整类类型。 -
Base
是Derived
的基类 - cv1 是不多于 cv2 的 cv 限定。
如果表达式 是空成员指针值,那么结果是目标类型 的空成员指针值。否则,结果是指向
Base
类的原(可能间接的)成员的指针。 如果不存在从“指向
Base
的 T
类型成员的指针”到“指向 Derived
的 T
类型成员的指针”的合法标准转换,那么程序非良构。 如果表达式 既不是空成员指针值,也不表示
Base
类的(可能间接的)成员,那么行为未定义。g) “指向 cv1 void 的指针”类型的右值 (C++11 前)纯右值 (C++11 起)在
T
是对象类型且 cv1 是不多于 cv2 的 cv 限定时可以显式转换到“指向 cv2 T
的指针”类型。
(C++17 前) | |
(C++17 起) |
同所有转换表达式,结果是:
- 左值,如果目标类型 是左值引用类型或到函数类型的右值引用类型 (C++11 起);
|
(C++11 起) |
- 否则是纯右值。
指针可以互相转换的对象
满足以下条件时,两个对象 a 与 b 的指针可以互相转换:
- 它们是同一个对象,或
- 一个是联合体对象而另一个是该对象的非静态数据成员,或
- 一个是标准布局的类对象,而另一个是该对象的首个非静态数据成员,或是该对象的任何基类子对象,或
- 存在对象 c 使得 a 与 c 的指针可以互相转换,而 c 与 b 的指针可以互相转换。
union U { int a; double b; } u; void* x = &u; // x 的值是“指向 u 的指针” double* y = static_cast<double*>(x); // y 的值是“指向 u.b 的指针” char* z = static_cast<char*>(x); // z 的值是“指向 u 的指针”
注解
使用 static_cast 进行的基类到派生类转换(向下转换)不会在运行时检查该对象的type#动态类型确实为 D
,因此它只能在该前提条件通过其他方法保证时,例如在实现静态多态时,才能安全使用。安全的向下转换可以用 dynamic_cast
执行。
static_cast 也能用来通过进行到指定类型的函数到指针转换来消解函数重载的歧义,如
std::for_each(files.begin(), files.end(), static_cast<std::ostream&(*)(std::ostream&)>(std::flush));
关键词
示例
运行此代码
#include <iostream> #include <vector> struct B { int m = 42; const char* hello() const { return "Hello world,这里是 B!\n"; } }; struct D : B { const char* hello() const { return "Hello world,这里是 D!\n"; } }; enum class E { ONE = 1, TWO, THREE }; enum EU { ONE = 1, TWO, THREE }; int main() { // 1. 静态向下转换 D d; B& br = d; // 通过隐式转换向上转换 std::cout << "1) " << br.hello(); D& another_d = static_cast<D&>(br); // 向下转换 std::cout << "1) " << another_d.hello(); // 3. 左值到亡值 std::vector<int> v0{1, 2, 3}; std::vector<int> v2 = static_cast<std::vector<int>&&>(v0); std::cout << "3) 移动后,v0.size() = " << v0.size() << '\n'; // 4. 弃值表达式 static_cast<void>(v2.size()); // 5. 初始化转换 int n = static_cast<int>(3.14); std::cout << "5) n = " << n << '\n'; std::vector<int> v = static_cast<std::vector<int>>(10); std::cout << "5) v.size() = " << v.size() << '\n'; // 6. 隐式转换的逆转换 void* nv = &n; int* ni = static_cast<int*>(nv); std::cout << "6) *ni = " << *ni << '\n'; // 7a. 有作用域枚举到 int 或 float E e = E::TWO; int two = static_cast<int>(e); std::cout << "7a) " << two << '\n'; // 7b. int 到枚举,枚举到另一枚举 E e2 = static_cast<E>(two); [[maybe_unused]] EU eu = static_cast<EU>(e2); // 7f. 指向成员指针向上转换 int D::*pm = &D::m; std::cout << "7f) " << br.*static_cast<int B::*>(pm) << '\n'; // 7g. void* 到任意对象指针 void* voidp = &e; [[maybe_unused]] std::vector<int>* p = static_cast<std::vector<int>*>(voidp); }
输出:
1) Hello world,这里是 B! 1) Hello world,这里是 D! 3) 移动后,v0.size() = 0 5) n = 3 5) v.size() = 10 6) *ni = 3 7a) 2 7f) 42
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 137 | C++98 | 指向 void 的指针的常量性或易变性可以被去除 | 不能通过 static_cast 去除 |
CWG 427 | C++98 | 向下转换与直接初始化之间有歧义 | 此时选择向下转换 |
CWG 439 | C++98 | 当指向对象的指针先转换到指向 void 的指针再转换回自身时, 它的值只有在目标类型有相同的 cv 限定时才会保留 |
允许 cv 限定不同 |
CWG 1094 | C++98 | 未指明浮点数到枚举的转换 | 指明转换规则 |
CWG 1320 | C++11 | 未指明有作用域枚举到 bool 的转换 | 指明转换规则 |
CWG 1412 | C++98 | 从“指向 void 的指针”转换到“指向对象的指针”的结果不明确 | 使之明确 |
CWG 1447 | C++11 | 未指明位域到右值引用的转换(无法绑定引用到位域) | 指明转换规则 |
CWG 1766 | C++98 | 整数或枚举到(另一)枚举的转换的结果在表达式 超出(结果枚举的)范围时未指定 | 此时行为未定义 |
CWG 1832 | C++98 | 整数或枚举到(另一)枚举的转换允许目标类型 不完整 | 此时结果枚举类型必须完整 |
CWG 2224 | C++98 | 从具有基类类型的成员到它的具有派生类类型的完整对象的转换合法 | 此时行为未定义 |
CWG 2254 | C++11 | 无数据成员的标准布局类对象可指针间转换到它的首个基类 | 可指针间转换到它的任意基类 |
CWG 2284 | C++11 | 联合体对象和该对象的非静态数据成员的指针不可互相转换 | 可以互相转换 |
CWG 2310 | C++98 | 基类到派生类的指针转换和派生类到基类的成员指针转换不需要派生类是完整类型 | 必须是完整类型 |
CWG 2338 | C++11 | 到底层类型固定的枚举类型的转换在表达式 超出范围时的行为未定义 | 先转换到底层类型 (不会有未定义行为) |
CWG 2499 | C++11 | 标准布局类可以有非指针可互转换的基类,即使所有基类子对象均拥有相同地址 | 它没有 |
CWG 2718 | C++98 | 基类到派生类的引用转换不需要派生类是完整类型 | 必须是完整类型 |
CWG 2882 | C++98 | 不明确 static_cast<void>(expr) 是否 会尝试组成从 expr 到 void 的隐式转换序列 |
此时不会尝试 |
引用
- C++23 标准(ISO/IEC 14882:2024):
- 7.6.1.9 Static cast [expr.static.cast]
- C++20 标准(ISO/IEC 14882:2020):
- 7.6.1.8 Static cast [expr.static.cast]
- C++17 标准(ISO/IEC 14882:2017):
- 8.2.9 Static cast [expr.static.cast]
- C++14 标准(ISO/IEC 14882:2014):
- 5.2.9 Static cast [expr.static.cast]
- C++11 标准(ISO/IEC 14882:2011):
- 5.2.9 Static cast [expr.static.cast]
- C++98 标准(ISO/IEC 14882:1998):
- 5.2.9 Static cast [expr.static.cast]
- C++03 标准(ISO/IEC 14882:2003):
- 5.2.9 Static cast [expr.static.cast]