static_cast 转换

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

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

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

满足以下条件时,两个对象 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, this is B!\n";
    }
};
struct D : B {
    void hello() const {
        std::cout << "Hello world, this is 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 << "after move, 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, this is B!
Hello world, this is D!
after move, v.size() = 0
*ni = 3
1
0

缺陷报告

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

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

参阅