Namespaces
Variants

static_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

通过隐式转换和用户定义转换的组合实现类型间的转换。

目录

语法

static_cast< 目标类型  >( 表达式  )

返回类型为 target-type 的值。

说明

只有以下转换可以通过 static_cast 实现,除非这些转换会 去除常量性 (或易变性)。

1) expression 是“ cv1 Base ”类型的左值,且 target-type 是“引用到 cv2 Derived ”类型,则当满足以下全部条件时,结果指向包含 expression Derived 类型对象:
  • Derived 是完整类类型
  • Base Derived 的基类
  • cv1 的cv限定符级别不高于 cv2
若满足以下任一条件,则程序非良构:
  • Base Derived 虚基类
  • Base Derived 的虚基类的基类。
  • 不存在从“指向 Derived 的指针”到“指向 Base 的指针”的有效 标准转换
如果 expression 实际上并非 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 起)
4) target-type 为(可能带有 cv 限定符的) void 类型,则转换不产生结果。此时, expression 被舍弃值表达式
5) 否则,若满足以下条件, expression 可显式转换为 target-type

声明 target-type temp ( expression  ) ; 对某个虚构临时变量 temp 是良构的。

此显式转换的效果等同于执行该声明与初始化,随后将 temp 用作转换结果。当且仅当初始化将 expression  用作 左值 (C++11 前) 泛左值 (C++11 起) 时,该表达式才会被用作 左值 (C++11 前) 泛左值 (C++11 起)

(C++17 前)

满足以下任意条件:

  • 存在从 expression target-type 的隐式转换序列。
  • 对类型为 target-type 的对象或引用进行从 expression 直接初始化 重载决议 能找到至少一个可行函数。
  • target-type 聚合类型 且其首元素为 x ,且存在从 expression x 类型的隐式转换序列。
(C++20 起)

显式转换定义如下:

  • target-type 是引用类型,则效果等同于对某个虚构临时变量 temp 执行声明与初始化 target-type temp ( expression  ) ; ,随后将 temp 用作转换结果。
  • 否则,结果对象从 expression  直接初始化。
(C++17 起)
6) 否则,若从 expression target-type 的转换是标准转换序列的逆过程,且该转换序列不包含以下任何转换,则可通过 static_cast 执行转换:
(C++17 起)
如果程序使用 static_cast 执行不符合规范的标准转换序列的逆操作,则该程序是不符合规范的。
7) 否则,将对 expression 应用左值到右值、数组到指针和函数到指针的转换。经过这些转换后, static_cast 仅能执行以下转换:
a) 作用域枚举 类型的值可以转换为整数或浮点类型。
  • target-type 是(可能 cv 限定的) bool ,则当 expression 的原始值为零时结果为 false ,其他所有值的结果为 true
  • target-type 是除(可能 cv 限定的) bool 外的整数类型,则当 expression 的原始值可由 target-type 表示时数值保持不变。否则结果值未指定。
(until C++20)
  • target-type 是整数类型,结果等同于先转换为枚举的底层类型再转换为 target-type 的结果。
(since C++20)
  • target-type 是浮点类型,结果等同于从原始值转换为 target-type 的结果。
(since C++11)
b) 整型或枚举类型的值可转换为任何完整枚举类型。
  • 目标类型 具有固定底层类型,则 表达式 首先通过 整型提升 整型转换 (如有必要)转换为该类型,再转换为 目标类型
  • 目标类型 不具有固定底层类型,当原始值 在枚举值范围内 表达式 的值保持不变,否则行为未定义。
c) 浮点类型的值也可以转换为任何完整枚举类型。其结果等同于先将 表达式 的原始值 转换 目标类型 的底层类型,再转换为 目标类型 本身。
d) 浮点类型的纯右值可显式转换为其他任意浮点类型。
  • 表达式 的源值能在 目标类型 中精确表示,则值不改变。
  • 否则,若 表达式 的源值介于 目标类型 的两个可表示值之间,则转换结果由实现定义选择二者之一。 [2]
  • 否则,行为未定义。
(since C++23)
e) 一个右值 (C++11 前) 一个纯右值 (C++11 起) ,其类型为“指向 cv1 Base 的指针”,可以显式转换为类型“指向 cv2 Derived 的指针”,当满足以下所有条件时:
  • Derived 是完整类类型。
  • Base Derived 的基类。
  • cv1 的 cv 限定符不比 cv2 更严格。
如果 expression 空指针值 ,则结果为 target-type 类型的空指针值。否则,结果是指向包含 expression 所指向的 Base 类型对象的 Derived 类型对象的指针。
若满足以下任一条件,则程序非良构:
  • Base Derived 虚基类
  • Base Derived 的虚基类的基类。
  • 不存在从“指向 Derived 的指针”到“指向 Base 的指针”的有效标准转换。
如果 expression 不是空指针值且实际上并未指向 Derived 类型对象的基类子对象,则行为未定义。
f) 类型为“指向 Derived cv1 T 类型成员的指针”的右值 (C++11 前) 类型为“指向 Derived cv1 T 类型成员的指针”的纯右值 (C++11 起) 在满足下列所有条件时,可显式转换为类型“指向 Base cv2 T 类型成员的指针”:
  • Derived 是完整类类型。
  • Base Derived 的基类。
  • cv1 的 cv 限定符级别不高于 cv2
如果 expression 是空成员指针值,则结果为 target-type 类型的空成员指针值。否则,结果是指向 Base 类原始(可能间接)成员的指针。
如果不存在从“指向 Base 类型为 T 的成员指针”到“指向 Derived 类型为 T 的成员指针”的有效标准转换,则程序非良构。
如果 expression 不是空成员指针值且其指向的成员不是 Base 类的(可能间接)成员,则行为未定义。
g) 右值 (C++11 前) 纯右值 (C++11 起) 类型为“指向 cv1 void 的指针”可以显式转换为类型“指向 cv2 T 的指针”,如果 T 是对象类型且 cv1 的 cv 限定不比 cv2 更严格。
  • 如果 表达式 是空指针值,则结果是类型为 目标类型 的空指针值。
  • 如果 表达式 表示内存中某个字节 的地址 A ,且 A 满足 T 对齐 要求,则结果指针值也表示 A
  • 任何其他此类指针转换的结果是未指定的。
  • 如果 表达式 是先前从类型“指向 cv3 T 的指针”对象的转换结果,则结果具有原始值。
(C++17 前)
  • 如果 表达式 表示内存中某个字节 的地址 A ,但 A 不满足 T 对齐 要求,则结果指针值是未指定的。
  • 否则,如果 表达式 指向对象 a ,且存在类型为 T (忽略 cv 限定)的对象 b a 指针可互转换(见下文),则结果是指向 b 的指针。
  • 否则,指针值在转换中保持不变。
(C++17 起)

与所有强制转换表达式一样,结果是:

  • target-type 是左值引用类型 或函数类型的右值引用 (C++11 起) ,则为左值;
  • target-type 是对象类型的右值引用,则为 xvalue;
(since C++11)
  • 否则为纯右值。
  1. 这种类型的 static_cast 用于在 std::move 中实现移动语义。
  2. 如果支持IEEE算术,默认采用四舍五入到最接近值。

指针可互转换对象

两个对象 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));

关键词

static_cast

示例

#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]

参见