static_cast 转换

来自cppreference.com
< cpp‎ | language

用隐式和用户定义转换的组合在类型间转换。

语法

static_cast < 新类型 > ( 表达式 )

返回 新类型 类型的值。

解释

只有下列转换在不去除常量性易变性的场合才能用 static_cast 执行。

1) 如果存在从 表达式新类型隐式转换序列,或者如果针对以 表达式新类型 类型的对象或引用所进行的直接初始化的重载决议找到至少一个可行函数,则 static_cast<新类型>(表达式) 返回如同以 新类型 Temp(表达式); 所初始化的一个虚构变量 Temp,它可能涉及隐式转换,对 新类型构造函数的调用,或对用户定义转换运算符的调用。对于非引用的 新类型static_cast 纯右值表达式的结果对象是其直接初始化的对象。 (C++17 起)
2) 如果 新类型 是指向某类类型 D 的指针或引用,且 表达式 的类型是其非虚基类 B 的左值或指向它的指针纯右值,则 static_cast 进行向下转型(downcast)。如果 BD 的有歧义的,不可访问的或虚的基类(或虚基类的基类),则此向下转型非良构。这种 static_cast 并不在运行时检查该对象的运行时类型确实为 D,因此它只能在该前提条件通过其他方法保证时,例如在实现静多态时,才能安全使用。安全的向下转型可以用 dynamic_cast 执行。
struct B { };
struct D : B { };
D d;
B& br = d;
static_cast<D&>(br); // 该左值指代原来的 d 对象
3) 如果 新类型 是右值引用类型,则 static_cast泛左值、类纯右值或数组纯右值 (C++17 前)任何左值 (C++17 起) 表达式 的值转换为与该表达式指代相同对象,或指代其基类子对象(取决于 新类型)的亡值。如果目标类型是表达式的不可访问或有歧义基类,则程序非良构。如果表达式是位域左值,则它首先被转换成底层类型的纯右值。这种 static_cast 用于在 std::move 中实现移动语义。
(C++11 起)
4) 如果 新类型 是(可为 cv 限定的)void 类型,则 static_cast 在求值 表达式 后舍弃其值。
5) 如果存在从 新类型表达式 类型的标准转换序列,且它不包含左值到右值、数组到指针、函数到指针、空指针、空成员指针、函数指针 (C++17 起)或布尔转换,则 static_cast 能进行该隐式转换的逆转换。
6) 如果从 表达式新类型 的转换涉及左值到右值、数组到指针或函数到指针转换,则 static_cast 可以显式执行该转换。
7) 有作用域枚举类型(C++11)能转换成整数或浮点类型。

当目标类型为(可为 cv 限定的)bool 类型时,如果原值为零则结果为 false,而对所有其他值结果为 true。对于其余整型类型,若该枚举的值能以目标类型表示,则结果为其值,否则结果未指明。

(C++20 前)

其结果与从枚举的底层类型隐式转换成目标类型的结果相同。

(C++20 起)
8) 整数或枚举类型值可转换为任何完整枚举类型
  • 如果底层类型不固定,则当 表达式 的值落在范围(范围是大到足以保有目标枚举的所有枚举项的最小位域的所有可能值)外时,则行为未定义。
  • 如果底层类型固定,则其结果与转换原值到枚举的底层类型再到该枚举类型的结果相同。
浮点类型的值也可转换为任何完整枚举类型。
  • 其结果与转换原值到枚举的底层类型再到该枚举类型的结果相同。
9) 指向某类 D 的成员的指针可以向上转型(upcast)为指向其无歧义、可访问的基类 B 的成员。这种 static_cast 不检查所指向对象的运行时类型是否包含该成员。
10) 指向(可为 cv 限定的)void 的指针类型的纯右值可转换到指向任何对象的指针类型。如果原指针值所表示的内存中字节地址不满足目标类型的对齐要求,则结果指针值未指明。否则,如果原指针值指向对象 a,且存在与 a 指针可互转换(定义于下文)的目标类型(忽略 cv 限定)的对象 b,则结果为指向 b 的指针。否则指针值不改变。任何指针转换到 void 指针再转换回原(或更为 cv 限定的)类型的指针,都保持其原值。

同所有转型表达式,结果是:

  • 左值,如果 新类型 是左值引用或到函数类型的右值引用;
  • 亡值,如果 新类型 是到对象类型的右值引用;
  • 否则为纯右值。

满足以下条件时,两个对象 ab 指针可互转换(pointer-interconvertible)

  • 它们是同一个对象,或
  • 一个是 union 对象而另一个是该对象的非静态数据成员,或
  • 一个是标准布局的类对象,而另一个是该对象的首个非静态数据成员,或如果该对象没有非静态数据成员,则为该对象的任何基类子对象,或
  • 存在对象 c 使得 ac 指针可互转换,而 cb 指针可互转换。
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 也能用于通过进行到指定类型的函数到指针转换,来消解函数重载的歧义,如

std::for_each(files.begin(), files.end(),
              static_cast<std::ostream&(*)(std::ostream&)>(std::flush));

关键词

static_cast

示例

#include <vector>
#include <iostream>
 
struct B {
    int m = 0;
    void hello() const {
        std::cout << "Hello world,这里是 B!\n";
    }
};
struct D : B {
    void hello() const {
        std::cout << "Hello world,这里是 D!\n";
    }
};
 
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
 
int main()
{
    // 1: 初始化转换
    int n = static_cast<int>(3.14); 
    std::cout << "n = " << n << '\n';
    std::vector<int> v = static_cast<std::vector<int>>(10);
    std::cout << "v.size() = " << v.size() << '\n';
 
    // 2: 静态向下转型
    D d;
    B& br = d; // 通过隐式转换向上转型
    br.hello();
    D& another_d = static_cast<D&>(br); // 向下转型
    another_d.hello();
 
    // 3: 左值到右值
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
    std::cout << "移动后,v.size() = " << v.size() << '\n';
 
    // 4: 弃值表达式
    static_cast<void>(v2.size());
 
    // 5. 隐式转换的逆
    void* nv = &n;
    int* ni = static_cast<int*>(nv);
    std::cout << "*ni = " << *ni << '\n';
 
    // 6. 数组到指针后随向上转型
    D a[10];
    B* dp = static_cast<B*>(a);
 
    // 7. 有作用域枚举到 int 或 float
    E e = E::ONE;
    int one = static_cast<int>(e);
    std::cout << one << '\n';
 
    // 8. int 到枚举,枚举到另一枚举
    E e2 = static_cast<E>(one);
    EU eu = static_cast<EU>(e2);
 
    // 9. 指向成员指针向上转型
    int D::*pm = &D::m;
    std::cout << br.*static_cast<int B::*>(pm) << '\n';
 
    // 10. void* 到任何类型
    void* voidp = &e;
    std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}

输出:

n = 3
v.size() = 10
Hello world,这里是 B!
Hello world,这里是 D!
移动后,v.size() = 0
*ni = 3
1
0

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

DR 应用于 出版时的行为 正确行为
CWG 2254 C++11 无数据成员的标准布局类对象可指针间转换到其首个基类 可指针间转换到其任何基类

参阅