名稱空間
變體
操作

static_cast 轉換

來自 cppreference.com
< 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]

[編輯] 另請參閱