static_cast
conversion
通过隐式转换和用户定义转换的组合实现类型间的转换。
目录 |
语法
static_cast<
目标类型
>(
表达式
)
|
|||||||||
返回类型为 target-type 的值。
说明
只有以下转换可以通过 static_cast 实现,除非这些转换会 去除常量性 (或易变性)。
Base
”类型的左值,且
target-type
是“引用到
cv2
Derived
”类型,则当满足以下全部条件时,结果指向包含
expression
的
Derived
类型对象:
-
Derived是完整类类型 -
Base是Derived的基类 - cv1 的cv限定符级别不高于 cv2
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); // UB:“b”子对象不是基类子对象
|
2)
若
目标类型
为“指向
Derived
的右值引用”且
表达式
为类型“(可能带 cv 限定)
Base
”的 xvalue,且
Base
是
Derived
的基类,则此类转换的结果与约束条件与“
Base
左值到
Derived
引用”转换相同。
3)
若
目标类型
为右值引用类型且被引用类型与
表达式
类型
引用兼容
,则
static_cast
将
泛左值、类纯右值或数组纯右值
(C++17 前)
任何左值
(C++17 起)
表达式
的值转换为指代相同对象或其基类子对象的 xvalue(取决于
目标类型
)。
[1]
若
目标类型
是
表达式
类型的不可访问或歧义基类,则程序非良构。
|
(C++11 起) |
|
声明 target-type temp ( expression ) ; 对某个虚构临时变量 temp 是良构的。 此显式转换的效果等同于执行该声明与初始化,随后将 temp 用作转换结果。当且仅当初始化将 expression 用作 左值 (C++11 前) 泛左值 (C++11 起) 时,该表达式才会被用作 左值 (C++11 前) 泛左值 (C++11 起) 。 |
(C++17 前) | ||
|
满足以下任意条件:
显式转换定义如下:
|
(C++17 起) |
| (C++17 起) |
|
a)
作用域枚举
类型的值可以转换为整数或浮点类型。
|
(since C++11) |
|
d)
浮点类型的纯右值可显式转换为其他任意浮点类型。
|
(since C++23) |
Base
的指针”,可以显式转换为类型“指向
cv2
Derived
的指针”,当满足以下所有条件时:
-
Derived是完整类类型。 -
Base是Derived的基类。 - cv1 的 cv 限定符不比 cv2 更严格。
Base
类型对象的
Derived
类型对象的指针。
-
Base是Derived的 虚基类 。 -
Base是Derived的虚基类的基类。 -
不存在从“指向
Derived的指针”到“指向Base的指针”的有效标准转换。
Derived
类型对象的基类子对象,则行为未定义。
Derived
类
cv1
T
类型成员的指针”的右值
(C++11 前)
类型为“指向
Derived
类
cv1
T
类型成员的指针”的纯右值
(C++11 起)
在满足下列所有条件时,可显式转换为类型“指向
Base
类
cv2
T
类型成员的指针”:
-
Derived是完整类类型。 -
Base是Derived的基类。 - cv1 的 cv 限定符级别不高于 cv2 。
Base
类原始(可能间接)成员的指针。
Base
类型为
T
的成员指针”到“指向
Derived
类型为
T
的成员指针”的有效标准转换,则程序非良构。
Base
类的(可能间接)成员,则行为未定义。
T
的指针”,如果
T
是对象类型且
cv1
的 cv 限定不比
cv2
更严格。
| (C++17 前) | |
| (C++17 起) |
与所有强制转换表达式一样,结果是:
- 若 target-type 是左值引用类型 或函数类型的右值引用 (C++11 起) ,则为左值;
|
(since 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
进行基类到派生类的转换(
向下转型
)时不会执行运行时检查来确保指针/引用对象的
动态类型
是
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. 静态向下转型 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. 有作用域枚举转整型 E e = E::TWO; int two = static_cast<int>(e); std::cout << "7a) " << two << '\n'; // 7b. 整型转枚举,枚举转另一枚举 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, 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 |
指向
void
的指针的常量性和易变性
可以被转换掉 |
在此类情况下不能
转换掉cv限定符 |
| 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 静态转换 [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]