static_cast
轉換
來自 cppreference.com
使用隱式和使用者定義轉換的組合進行型別轉換。
目錄 |
[編輯] 語法
static_cast< 目標型別 >( 表示式 ) |
|||||||||
返回 目標型別 的值。
[編輯] 解釋
只有以下轉換可以使用 static_cast 完成,除非這些轉換會 丟棄 constness (或 volatility)。
1) 如果 表示式 是“cv1
Base
”型別的左值,而 目標型別 是“對 cv2 Derived
的引用”,則結果引用包含 表示式 的 Derived
型別的物件,如果所有以下條件都滿足:-
Derived
是一個完整類型別。 -
Base
是Derived
的基類。 - cv1 不比 cv2 具有更大的 cv-限定。
如果以下任何條件滿足,則程式非良構:
如果 表示式 實際上不是
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 ”型別的亡值,使得 Base 是 Derived 的基類,則此轉換的結果和約束與“Base 左值到 Derived 引用”轉換相同。3) 如果 目標型別 是右值引用型別,且引用型別與 表示式 的型別引用相容,則 static_cast 將 glvalue、類純右值或陣列純右值(C++17 前)任何左值(C++17 起) 表示式 的值轉換為指向與表示式相同物件或其基類子物件(取決於 目標型別)的亡值。[1]
如果 目標型別 是 表示式 型別的不可訪問或有歧義的基類,則程式非良構。
如果 表示式 是位域左值,它首先被轉換為底層型別的純右值。
|
(C++11 起) |
5) 否則,如果滿足以下條件,表示式 可以顯式轉換為 目標型別:
宣告 目標型別 temp(表示式 ); 對於某個虛構的臨時變數 temp 是良構的。 這種顯式轉換的效果與執行宣告和初始化,然後使用 temp 作為轉換結果相同。表示式 僅當初始化將其用作 左值(C++11 前)glvalue(C++11 起) 時才被用作 左值(C++11 前)glvalue(C++11 起)。 |
(C++17 前) | ||
以下任何條件滿足:
顯式轉換定義如下:
|
(C++17 起) |
6) 否則,如果從 表示式 到 目標型別 的轉換是標準轉換序列的逆,且轉換序列不包含以下任何轉換,則可以透過 static_cast 執行轉換:
(C++17 起) |
如果程式使用 static_cast 執行非良構標準轉換序列的逆,則程式非良構。
7) 否則,將左值到右值、陣列到指標和函式到指標轉換應用於 表示式。在這些轉換之後,只有以下轉換可以透過 static_cast 執行:
a) 限定作用域列舉型別的值可以轉換為整數或浮點型別。
|
(C++11 起) |
b) 整數或列舉型別的值可以轉換為任何完整的列舉型別。
d) 浮點型別的純右值可以顯式轉換為任何其他浮點型別。
|
(C++23 起) |
e) “指向 cv1
Base
的指標”型別的右值(C++11 前)純右值(C++11 起)可以顯式轉換為“指向 cv2 Derived
的指標”型別,如果所有以下條件都滿足:-
Derived
是一個完整類型別。 -
Base
是Derived
的基類。 - cv1 不比 cv2 具有更大的 cv-限定。
如果以下任何條件滿足,則程式非良構:
-
Base
是Derived
的虛基類。 -
Base
是Derived
的虛基類的基類。 - 不存在從“指向
Derived
的指標”到“指向Base
的指標”的有效標準轉換。
如果 表示式 不是空指標值,並且實際上不指向
Derived
型別物件的基類子物件,則行為未定義。f) “指向
Derived
的成員 cv1 T
的指標”型別的右值(C++11 前)純右值(C++11 起)可以顯式轉換為“指向 Base
的成員 cv2 T
的指標”型別,如果所有以下條件都滿足:-
Derived
是一個完整類型別。 -
Base
是Derived
的基類。 - cv1 不比 cv2 具有更大的 cv-限定。
如果 表示式 是空成員指標值,則結果是 目標型別 的空成員指標值。否則,結果是指向類
Base
的原始(可能間接)成員的指標。 如果不存在從“指向
Base
的成員 T
的指標”到“指向 Derived
的成員 T
的指標”的有效標準轉換,則程式非良構。 如果 表示式 不是空成員指標值,並且它表示的成員不是類
Base
的(可能間接)成員,則行為未定義。g) “指向 cv1 void 的指標”型別的右值(C++11 前)純右值(C++11 起)可以顯式轉換為“指向 cv2
T
的指標”型別,如果 T
是物件型別,且 cv1 不比 cv2 具有更大的 cv-限定。(C++17 前) | |
(C++17 起) |
與所有轉換表示式一樣,結果是
- 如果 目標型別 是左值引用型別或函式型別的右值引用(C++11 起),則為左值;
|
(C++11 起) |
- 否則為純右值。
[編輯] 指標可互轉換物件
兩個物件 a 和 b 是指標可互轉換的,如果:
- 它們是同一個物件,或
- 其中一個是聯合物件,另一個是該物件的非靜態資料成員,或
- 其中一個是標準佈局類物件,另一個是該物件的第一個非靜態資料成員或該物件的任何基類子物件,或
- 存在一個物件 c,使得 a 和 c 是指標可互轉換的,並且 c 和 b 是指標可互轉換的。
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));
[編輯] 關鍵詞
[編輯] 示例
執行此程式碼
#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) 是否嘗試 從 expr 到 void 形成隱式轉換序列 |
在這種情況下不嘗試 |
[編輯] 參考文獻
- 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]