命名空间
变体
操作

static_cast 转换

来自 cppreference.cn
< cpp‎ | 语言
 
 
C++ 语言
 
 

使用隐式和用户定义转换的组合进行类型转换。

目录

[编辑] 语法

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

返回 目标类型 的值。

[编辑] 解释

只有以下转换可以使用 static_cast 完成,除非这些转换会 丢弃 constness (或 volatility)。

1) 如果 表达式 是“cv1 Base”类型的左值,而 目标类型 是“对 cv2 Derived 的引用”,则结果引用包含 表达式Derived 类型的对象,如果所有以下条件都满足:
  • Derived 是一个完整类类型。
  • BaseDerived 的基类。
  • cv1 不比 cv2 具有更大的 cv-限定。
如果以下任何条件满足,则程序非良构:
  • BaseDerived虚基类
  • BaseDerived 的虚基类的基类。
  • 不存在从“指向 Derived 的指针”到“指向 Base 的指针”的有效标准转换
如果 表达式 实际上不是 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”类型的亡值,使得 BaseDerived 的基类,则此转换的结果和约束与“Base 左值到 Derived 引用”转换相同。
3) 如果 目标类型 是右值引用类型,且引用类型与 表达式 的类型引用兼容,则 static_castglvalue、类纯右值或数组纯右值(C++17 前)任何左值(C++17 起) 表达式 的值转换为指向与表达式相同对象或其基类子对象(取决于 目标类型)的亡值。[1]
如果 目标类型表达式 类型的不可访问或有歧义的基类,则程序非良构。
如果 表达式位域左值,它首先被转换为底层类型的纯右值。
(C++11 起)
4) 如果 目标类型 是(可能 cv-限定的)void,则转换没有结果。在这种情况下,表达式 是一个废弃值表达式
5) 否则,如果满足以下条件,表达式 可以显式转换为 目标类型

声明 目标类型 temp(表达式 ); 对于某个虚构的临时变量 temp 是良构的。

这种显式转换的效果与执行声明和初始化,然后使用 temp 作为转换结果相同。表达式 仅当初始化将其用作 左值(C++11 前)glvalue(C++11 起) 时才被用作 左值(C++11 前)glvalue(C++11 起)

(C++17 前)

以下任何条件满足:

  • 存在从 表达式目标类型 的隐式转换序列。
  • 表达式 直接初始化 目标类型 的对象或引用的重载决议会找到至少一个可行的函数。
  • 目标类型 是一个聚合类型,其第一个元素为 x,并且存在从 表达式x 类型的隐式转换序列。
(C++20 起)

显式转换定义如下:

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

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

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

[编辑] 指针可互转换对象

两个对象 ab指针可互转换的,如果:

  • 它们是同一个对象,或
  • 其中一个是联合对象,另一个是该对象的非静态数据成员,或
  • 其中一个是标准布局类对象,另一个是该对象的第一个非静态数据成员或该对象的任何基类子对象,或
  • 存在一个对象 c,使得 ac 是指针可互转换的,并且 cb 是指针可互转换的。
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));

[编辑] 关键词

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. 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) 是否尝试
exprvoid 形成隐式转换序列
在这种情况下不尝试

[编辑] 参考文献

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

[编辑] 另请参阅