static_cast
转换
来自 cppreference.cn
使用隐式和用户定义的转换的组合在类型之间转换。
内容 |
[编辑] 语法
static_cast< target-type >( expression ) |
|||||||||
返回 target-type 类型的值。
[编辑] 解释
只有以下转换可以使用 static_cast 完成,除非此类转换会移除 constness(或 volatility)。
1) 如果 expression 是 “cv1
Base
” 类型的左值,并且 target-type 是 “cv2 Derived
的引用”,则如果满足以下所有条件,则结果引用类型为 Derived
的对象,该对象包含 expression-
Derived
是一个完整的类类型。 -
Base
是Derived
的基类。 - cv1 不是比 cv2 更强的 cv 限定符。
如果满足以下任何条件,则程序是非良构的
如果 expression 实际上不是
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) 如果 target-type 是 “
Derived 的右值引用”,并且 expression 是 “(可能带有 cv 限定符的) Base ” 类型的 xvalue,使得 Base 是 Derived 的基类,则此类转换的结果和约束与 “Base 左值到 Derived 引用的” 转换相同。3) 如果 target-type 是右值引用类型,并且被引用的类型与 expression 的类型引用兼容,则 static_cast 将 glvalue、类 prvalue 或数组 prvalue(C++17 前)任何左值(C++17 起) expression 的值转换为引用与表达式相同的对象或其基类子对象(取决于 target-type)的 xvalue。[1]
如果 target-type 是 expression 类型的不可访问或不明确的基类,则程序是非良构的。
如果 expression 是位域左值,则首先将其转换为底层类型的 prvalue。
|
(C++11 起) |
5) 否则,如果满足以下条件,则可以将 expression 显式转换为 target-type
声明 target-type temp(expression ); 对于某些发明的临时变量 temp 是良构的。 这种显式转换的效果与执行声明和初始化,然后使用 temp 作为转换结果的效果相同。expression 被用作 一个左值(C++11 前)一个 glvalue(C++11 起) 当且仅当初始化将其用作 一个左值(C++11 前)一个 glvalue(C++11 起)。 |
(C++17 前) | ||
满足以下任何条件
显式转换定义如下
|
(C++17 起) |
6) 否则,如果从 expression 到 target-type 的转换是标准转换序列的逆过程,并且该转换序列不包含以下任何转换,则可以使用 static_cast 执行转换
(C++17 起) |
如果程序使用 static_cast 执行非良构的标准转换序列的逆过程,则它是非良构的。
7) 否则,左值到右值、数组到指针和函数到指针的转换将应用于 expression。在这些转换之后,只有以下转换可以使用 static_cast 执行
a) 作用域枚举类型的值可以转换为整数或浮点类型。
|
(C++11 起) |
b) 整数或枚举类型的值可以转换为任何完整的枚举类型。
d) 浮点类型的 prvalue 可以显式转换为任何其他浮点类型。
|
(C++23 起) |
e) “指向 cv1
Base
的指针” 类型的右值(C++11 前)prvalue(C++11 起)可以显式转换为 “指向 cv2 Derived
的指针” 类型,如果满足以下所有条件-
Derived
是一个完整的类类型。 -
Base
是Derived
的基类。 - cv1 不是比 cv2 更强的 cv 限定符。
如果 expression 是空指针值,则结果是 target-type 类型的空指针值。否则,结果是指向类型为
Derived
的对象的指针,该对象包含 expression 指向的类型为 Base
的对象。 如果满足以下任何条件,则程序是非良构的
-
Base
是Derived
的虚基类。 -
Base
是Derived
的虚基类的基类。 - 不存在从 “指向
Derived
的指针” 到 “指向Base
的指针” 的有效标准转换。
如果 expression 不是空指针值,并且实际上并未指向类型为
Derived
的对象的基类子对象,则行为未定义。f) “指向
Derived
的成员,类型为 cv1 T
” 类型的右值(C++11 前)prvalue(C++11 起)可以显式转换为 “指向 Base
的成员,类型为 cv2 T
” 类型,如果满足以下所有条件-
Derived
是一个完整的类类型。 -
Base
是Derived
的基类。 - cv1 不是比 cv2 更强的 cv 限定符。
如果 expression 是空成员指针值,则结果是 target-type 类型的空成员指针值。否则,结果是指向类
Base
的原始(可能间接)成员的指针。 如果不存在从 “指向
Base
的成员,类型为 T
” 到 “指向 Derived
的成员,类型为 T
” 的有效标准转换,则程序是非良构的。 如果 expression 不是空成员指针值,并且它表示的成员不是类
Base
的(可能间接)成员,则行为未定义。g) “指向 cv1 void 的指针” 类型的右值(C++11 前)prvalue(C++11 起)可以显式转换为 “指向 cv2
T
的指针” 类型,如果 T
是对象类型且 cv1 不是比 cv2 更强的 cv 限定符。(C++17 前) | |
(C++17 起) |
与所有强制转换表达式一样,结果是
- 如果 target-type 是左值引用类型或函数类型的右值引用(C++11 起),则为左值;
|
(C++11 起) |
- 否则为 prvalue。
- ↑ static_cast 用于在 std::move 中实现移动语义。
- ↑
[编辑] 指针可互转换对象
如果满足以下条件,则两个对象 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++ 标准。
DR 应用于 已发布行为 正确行为 CWG 137 C++98 的 constness 和 volatility
指向 void 的指针可以被移除 constnesscv 限定符不能在
这种情况下被移除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 从整数或枚举值转换为枚举
如果 expression 超出范围,则值会产生未指定的结果行为是
在这种情况下未定义CWG 1832 C++98 从整数或枚举值转换为
枚举值允许 target-type 为不完整类型不允许 CWG 2224 C++98 从基类类型成员转换为
其派生类类型的完整对象是有效的行为是
在这种情况下未定义CWG 2254 C++11 没有数据成员的标准布局类对象
与其第一个基类是指针可互转换的它是指针可互转换的
与其任何基类CWG 2284 C++11 非标准布局联合体对象和非静态数据
该对象的成员不是指针可互转换的它们是 CWG 2310 C++98 对于基类到派生类指针转换和
派生类到基类成员指针转换,
派生类类型可能是不完整的必须是完整的 CWG 2338 C++11 转换为具有固定底层类型的枚举类型
如果 expression 超出范围,则会导致未定义的行为转换为底层类型
首先(没有未定义的行为)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 静态转换 [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]
[编辑] 参见