static_cast 转换

来自cppreference.com
< cpp‎ | language
 
 
 
表达式
概述
值类别(左值 lvalue、右值 rvalue、亡值 xvalue)
求值顺序(序列点)
常量表达式
潜在求值表达式
初等表达式
lambda 表达式(C++11)
字面量
整数字面量
浮点字面量
布尔字面量
字符字面量,包含转义序列
字符串字面量
空指针字面量(C++11)
用户定义字面量(C++11)
运算符
赋值运算符a=ba+=ba-=ba*=ba/=ba%=ba&=ba|=ba^=ba<<=ba>>=b
自增与自减++a--aa++a--
算术运算符+a-aa+ba-ba*ba/ba%b~aa&ba|ba^ba<<ba>>b
逻辑运算符a||ba&&b!a
比较运算符a==ba!=ba<ba>ba<=ba>=ba<=>b(C++20)
成员访问运算符a[b]*a&aa->ba.ba->*ba.*b
其他运算符a(...)a,ba?b:c
new 表达式
delete 表达式
throw 表达式
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
折叠表达式(C++17)
运算符的代用表示
优先级和结合性
运算符重载
默认比较(C++20)
类型转换
隐式转换
一般算术转换
const_cast
static_cast
reinterpret_cast
dynamic_cast
显式转换 (T)a, T(a)
用户定义转换
 

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

语法

static_cast< 新类型 >( 表达式 )

返回 新类型 类型的值。

解释

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

1) 如果 新类型 是到某个类类型 D 的引用并且 表达式 是类型为它的非虚基类 B 的左值,或者 新类型 是指向某个完整类类型 D 的指针并且 表达式 是类型为指向它的非虚基类 B 的指针纯右值,那么 static_cast 进行向下转型(downcast)。如果 BD 的有歧义的,不可访问的或虚的基类(或虚基类的基类),那么此向下转型非良构。

这种 static_cast 不会在运行时检查该对象的运行时类型确实为 D,因此它只能在该前提条件通过其他方法保证时,例如在实现静多态时,才能安全使用。安全的向下转型可以用 dynamic_cast 执行。如果 表达式 指代或指向的对象确实是具有 D 类型的对象的基类子对象,那么结果指代的是具有 D 类型的该外围对象。否则行为未定义:

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); // 未定义行为:子对象 b 不是基类子对象
2) 如果 新类型 是右值引用类型,那么 static_cast泛左值、类纯右值或数组纯右值 (C++17 前)任何左值 (C++17 起) 表达式 的值转换为与该表达式指代相同对象,或指代它的基类子对象(取决于 新类型)的亡值。如果目标类型是表达式的不可访问或有歧义的基类,那么程序非良构。如果表达式是位域左值,那么它会首先被转换成底层类型的纯右值。这种 static_cast 用于在 std::move 中实现移动语义。
(C++11 起)
3) 如果存在从 表达式新类型隐式转换序列,或者如果针对以 表达式新类型 类型的对象或引用所进行的直接初始化的重载决议找到至少一个可行函数,那么 static_cast<新类型>(表达式) 返回如同以 新类型 Temp(表达式); 所初始化的一个虚构变量 Temp,它可能涉及隐式转换,对 新类型构造函数的调用,或对用户定义转换运算符的调用。对于非引用的 新类型static_cast 纯右值表达式的结果对象是其直接初始化的对象。 (C++17 起)
4) 如果 新类型 是(可有 cv 限定的)void 类型,那么 static_cast 在求值 表达式 后舍弃该值。
5) 如果存在从 新类型表达式 类型的标准转换序列,且它不包含左值到右值、数组到指针、函数到指针、空指针、空成员指针、函数指针 (C++17 起)或布尔转换,那么 static_cast 能进行该隐式转换的逆转换。
6) 如果从 表达式新类型 的转换涉及左值到右值、数组到指针或函数到指针转换,那么 static_cast 可以显式执行该转换。
7) 有作用域枚举类型能转换到整数或浮点类型。

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

(C++20 前)

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

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

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

  • 左值,如果 新类型 是左值引用类型或到函数类型的右值引用类型 (C++11 起)
  • 亡值,如果 新类型 是到对象类型的右值引用类型;
(C++11 起)
  • 否则是纯右值。

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

  • 它们是同一个对象,或
  • 一个是联合体对象而另一个是该对象的非静态数据成员,或
  • 一个是标准布局的类对象,而另一个是该对象的首个非静态数据成员,或是该对象的任何基类子对象,或
  • 存在对象 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: 静态向下转型
    D d;
    B& br = d; // 通过隐式转换向上转型
    br.hello();
    D& another_d = static_cast<D&>(br); // 向下转型
    another_d.hello();
 
    // 2: 左值到右值
    std::vector<int> v0{1,2,3};
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v0);
    std::cout << "移动后,v.size() = " << v0.size() << '\n';
 
    // 3: 初始化转换
    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';
 
    // 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);
}

输出:

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

缺陷报告

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

缺陷报告 应用于 出版时的行为 正确行为
CWG 137 C++98 指向 void 的指针的常量性或易变性可以被去除 不能通过 static_cast 去除
CWG 439 C++98 当指向对象的指针先转换到指向 void 的指针再转换回自身时,
它的值只有在目标类型有相同的 cv 限定时才会保留
允许 cv 限定不同
CWG 1094 C++98 未指明浮点数到枚举的转换 指明转换规则
CWG 1320 C++11 未指明有作用域枚举到 bool 的转换 指明转换规则
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 标准布局类可以有非指针可互转换的基类,即使所有基类子对象均拥有相同地址 它没有

参阅