static_cast
转换
来自 cppreference.cn
使用隐式和用户定义转换的组合进行类型转换。
目录 |
[编辑] 语法
static_cast< 目标类型 >( 表达式 ) |
|||||||||
返回 目标类型 的值。
[编辑] 解释
只有以下转换可以使用 static_cast 完成,除非这些转换会 丢弃 constness (或 volatility)。
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, lvalue denoting the original “d” object static_cast<D&>(br2); // UB: the “b” subobject is not a base class subobject
2) 如果 目标类型 是“对
Derived 的右值引用”,且 表达式 是“(可能 cv-限定的) Base ”类型的亡值,使得 Base 是 Derived 的基类,则此转换的结果和约束与“Base 左值到 Derived 引用”转换相同。3) 如果 目标类型 是右值引用类型,且引用类型与 表达式 的类型引用兼容,则 static_cast 将 glvalue、类纯右值或数组纯右值(C++17 前)任何左值(C++17 起) 表达式 的值转换为指向与表达式相同对象或其基类子对象(取决于 目标类型)的亡值。[1]
如果 目标类型 是 表达式 类型的不可访问或有歧义的基类,则程序非良构。
如果 表达式 是位域左值,它首先被转换为底层类型的纯右值。
|
(C++11 起) |
5) 否则,如果满足以下条件,表达式 可以显式转换为 目标类型:
声明 目标类型 temp(表达式 ); 对于某个虚构的临时变量 temp 是良构的。 这种显式转换的效果与执行声明和初始化,然后使用 temp 作为转换结果相同。表达式 仅当初始化将其用作 左值(C++11 前)glvalue(C++11 起) 时才被用作 左值(C++11 前)glvalue(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-限定。
如果以下任何条件满足,则程序非良构:
-
Base
是Derived
的虚基类。 -
Base
是Derived
的虚基类的基类。 - 不存在从“指向
Derived
的指针”到“指向Base
的指针”的有效标准转换。
如果 表达式 不是空指针值,并且实际上不指向
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 起)可以显式转换为“指向 cv2
T
的指针”类型,如果 T
是对象类型,且 cv1 不比 cv2 具有更大的 cv-限定。(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's value is “pointer to u” double* y = static_cast<double*>(x); // y's value is “pointer to u.b” char* z = static_cast<char*>(x); // z's value is “pointer to u”
[编辑] 注意
使用 static_cast 进行基类到派生类转换(向下转型)不会进行运行时检查,以确保所指向/引用的对象的动态类型是 Derived
,并且只有在通过其他方式(例如在实现静态多态时)保证此前提条件时才能安全使用。安全的向下转型可以使用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, this is B!\n"; } }; struct D : B { const char* hello() const { return "Hello world, this is D!\n"; } }; enum class E { ONE = 1, TWO, THREE }; enum EU { ONE = 1, TWO, THREE }; int main() { // 1. static downcast D d; B& br = d; // upcast via implicit conversion std::cout << "1) " << br.hello(); D& another_d = static_cast<D&>(br); // downcast std::cout << "1) " << another_d.hello(); // 3. lvalue to xvalue std::vector<int> v0{1, 2, 3}; std::vector<int> v2 = static_cast<std::vector<int>&&>(v0); std::cout << "3) after move, v0.size() = " << v0.size() << '\n'; // 4. discarded-value expression static_cast<void>(v2.size()); // 5. initializing conversion 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. inverse of implicit conversion void* nv = &n; int* ni = static_cast<int*>(nv); std::cout << "6) *ni = " << *ni << '\n'; // 7a. scoped enum to int E e = E::TWO; int two = static_cast<int>(e); std::cout << "7a) " << two << '\n'; // 7b. int to enum, enum to another enum E e2 = static_cast<E>(two); [[maybe_unused]] EU eu = static_cast<EU>(e2); // 7f. pointer to member upcast int D::*pm = &D::m; std::cout << "7f) " << br.*static_cast<int B::*>(pm) << '\n'; // 7g. void* to any object pointer void* voidp = &e; [[maybe_unused]] std::vector<int>* p = static_cast<std::vector<int>*>(voidp); }
输出
1) Hello world, this is B! 1) Hello world, this is D! 3) after move, v0.size() = 0 5) n = 3 5) v.size() = 10 6) *ni = 3 7a) 2 7f) 42
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 137 | C++98 | constness 和 volatility 指向 void 的指针可以被去除 |
cv-限定符不能被 在这种情况下去除 |
CWG 427 | C++98 | 向下转型可能与直接初始化有歧义 | 在这种情况下选择向下转型 |
CWG 439 | C++98 | 当将“指向对象的指针”转换为“指向 void 的指针”,然后再转换回自身时,只有当结果类型具有相同的 cv-限定符时才能保留其值 cv-限定符 |
可能不同 可能不同 |
CWG 1094 | C++98 | 从浮点值到枚举值的转换 未指定 |
已指定 |
CWG 1320 | C++11 | 从限定作用域枚举值到布尔值的转换 未指定 |
已指定 |
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 静态转换 [expr.static.cast]
- C++20 标准 (ISO/IEC 14882:2020)
- 7.6.1.8 静态转换 [expr.static.cast]
- C++17 标准 (ISO/IEC 14882:2017)
- 8.2.9 静态转换 [expr.static.cast]
- C++14 标准 (ISO/IEC 14882:2014)
- 5.2.9 静态转换 [expr.static.cast]
- C++11 标准 (ISO/IEC 14882:2011)
- 5.2.9 静态转换 [expr.static.cast]
- C++98 标准 (ISO/IEC 14882:1998)
- 5.2.9 静态转换 [expr.static.cast]
- C++03 标准 (ISO/IEC 14882:2003)
- 5.2.9 静态转换 [expr.static.cast]