名稱空間
變體
操作

顯式型別轉換

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

使用顯式和隱式轉換的組合在型別之間進行轉換。

目錄

[編輯] 語法

( 型別識別符號 ) 一元表示式 (1)
簡單型別說明符 ( 表示式列表 (可選) )
簡單型別說明符 ( 初始化列表 (可選) )
(2) (C++11 前)
(C++11 起)
簡單型別說明符 { 初始化列表 (可選) } (3) (C++11 起)
簡單型別說明符 { 指定初始化列表 } (4) (C++20 起)
typename 識別符號 ( 初始化列表 (可選) ) (5) (C++11 起)
typename 識別符號 { 初始化列表 (可選) } (6) (C++11 起)
typename 識別符號 { 指定初始化列表 } (7) (C++20 起)

顯式地將任意數量的值轉換為目標型別的值。

1) 顯式型別轉換(C 風格型別轉換)。
2-7) 顯式型別轉換(函式風格型別轉換)。
型別標識 - 一個 型別識別符號
一元表示式 - 一個一元表示式(其頂層運算子的優先順序不高於 C 風格型別轉換)
簡單型別說明符 - 一個簡單型別說明符
表示式-列表 - 以逗號分隔的表示式列表(除了不帶括號的逗號表示式
初始化器-列表 - 以逗號分隔的初始化子句列表
指定初始化列表 - 以逗號分隔的指定初始化子句列表
識別符號 - 一個(可能帶有限定符的)識別符號(包括模板識別符號

[編輯] 解釋

1) 當遇到 C 風格型別轉換時,編譯器會嘗試按以下順序將其解釋為以下型別轉換表示式:
a) const_cast<型別識別符號 >(一元表示式 )
b) static_cast<型別識別符號 >(一元表示式 ),並擴充套件:指向派生類的指標或引用額外允許被轉換為指向無歧義基類的指標或引用(反之亦然),即使該基類是不可訪問的(即,此轉換忽略私有繼承說明符)。同樣適用於將成員指標轉換為指向無歧義非虛基類成員的指標;
c) 一個 static_cast(帶擴充套件)後跟一個 const_cast
d) reinterpret_cast<型別識別符號 >(一元表示式 )
e) 一個 reinterpret_cast 後跟一個 const_cast
即使第一個選擇是格式錯誤的,也會選擇滿足相應型別轉換運算子要求的第一個選擇(參見示例)。如果使用 static_cast 後跟 const_cast,並且該轉換可以以多種方式解釋,則該轉換是格式錯誤的。
此外,C 風格的型別轉換可以在指向不完整類型別的指標之間進行轉換。如果 型別識別符號一元表示式 的型別都是指向不完整類型別的指標,則未指定選擇 static_cast 還是 reinterpret_cast
2-7) 函式風格的型別轉換指定一個型別簡單型別說明符 識別符號 (C++11 起))和一個初始化器(其餘部分),它構造一個目標型別 T 的值,該型別由指定的型別和初始化器(C++17 起)確定。

T 是指定的型別。

(C++17 前)

T 的確定方式如下:

  • 如果指定的型別是推導類型別的佔位符,則 T 是透過類模板推導的過載解析選擇的函式的返回型別。
  • 否則,如果指定的型別包含佔位符型別,則 T 是推匯出的型別。
(C++23 起)
  • 否則,T 是指定的型別。
(C++17 起)
轉換結果的確定方式如下:
  • 如果函式風格型別轉換的語法為 (2),並且括號中只有一個表示式,則此型別轉換等同於相應的 C 風格型別轉換。
  • 否則,如果 T 是(可能帶有 cv 限定符的)void,則結果是型別 void一個右值(C++11 前)一個純右值(C++11 起),不執行初始化。
  • 如果初始化器不是 (),則程式格式錯誤。
(C++11 前)
  • 如果初始化器在包擴充套件(如果有)之後不是 (){},則程式格式錯誤。
(C++11 起)
  • 否則,如果 T 是引用型別,則函式風格型別轉換與從指定初始化器直接初始化一個虛構的型別 T 的變數 t 具有相同的效果,結果是被初始化的 t
  • 結果是一個左值。
(C++11 前)
  • 如果 T 是左值引用型別或函式型別的右值引用,則結果是左值。
  • 否則,結果是亡值。
(C++11 起)
  • 否則,結果是型別 T一個右值(C++11 前)一個純右值(C++11 起)指代一個臨時物件(C++17 前)其結果物件(C++17 起)使用指定初始化器直接初始化

[編輯] 歧義解析

[編輯] 歧義宣告語句

在帶有函式風格型別轉換表示式作為最左側子表示式的表示式語句與宣告語句之間存在歧義的情況下,透過將其視為宣告來解決歧義。這種消歧義純粹是語法上的:它不考慮語句中出現的名稱的含義,除了它們是否是型別名稱

struct M {};
struct L { L(M&); };
 
M n;
void f()
{
    M(m);    // declaration, equivalent to M m;
    L(n);    // ill-formed declaration, equivalent to L n;
    L(l)(m); // still a declaration, equivalent to L l((m));
}

然而,如果歧義宣告語句中最外層的宣告符具有尾隨返回型別,則該語句僅在尾隨返回型別以 auto 開頭時才被視為宣告語句。

struct M;
 
struct S
{
    S* operator()();
    int N;
    int M;
 
    void mem(S s)
    {
        auto(s)()->M; // expression (S::M hides ::M), invalid before C++23
    }
};
 
void f(S s)
{
    {
        auto(s)()->N; // expression, invalid before C++23
        auto(s)()->M; // function declaration, equivalent to M s();
    }
    {
        S(s)()->N;    // expression
        S(s)()->M;    // expression
    }
}
(C++11 起)

[編輯] 歧義函式引數

上述歧義也可能發生在宣告上下文中。在這種上下文中,選擇是在具有函式風格型別轉換作為初始化器的物件宣告與涉及函式宣告符的宣告之間,其中引數名稱周圍有一組多餘的括號。解決方案也是將任何可能作為宣告的構造(例如潛在的引數宣告)視為宣告。

struct S
{
    S(int);
};
 
void foo(double a)
{
    S w(int(a)); // function declaration: has a parameter `a` of type int
    S x(int());  // function declaration: has an unnamed parameter of type int(*)() 
                 // that is adjusted from int()
 
    // Ways to avoid ambiguity:
    S y((int(a))); // object declaration: extra pair of parentheses
    S y((int)a);   // object declaration: C-style cast
    S z = int(a);  // object declaration: no ambiguity for this syntax
}

然而,如果歧義引數宣告中最外層的宣告符具有尾隨返回型別,則只有當它以 auto 開頭時,歧義才會被解決為宣告。

typedef struct BB { int C[2]; } *B, C;
 
void foo()
{
    S a(B()->C);    // object declaration: B()->C cannot declare a parameter
    S b(auto()->C); // function declaration: has an unnamed parameter of type C(*)()
                    // that is adjusted from C()
}
(C++11 起)

[編輯] 歧義型別識別符號

函式風格型別轉換與型別識別符號之間的相似性可能導致歧義。解決方案是任何在其語法上下文中可能作為型別識別符號的構造都應被視為型別識別符號。

// `int()` and `int(unsigned(a))` can both be parsed as type-id:
// `int()`            represents a function returning int
//                    and taking no argument
// `int(unsigned(a))` represents a function returning int
//                    and taking an argument of type unsigned
void foo(signed char a)
{
    sizeof(int());            // type-id (ill-formed)
    sizeof(int(a));           // expression
    sizeof(int(unsigned(a))); // type-id (ill-formed)
 
    (int()) + 1;            // type-id (ill-formed)
    (int(a)) + 1;           // expression
    (int(unsigned(a))) + 1; // type-id (ill-formed)
}

然而,如果歧義型別識別符號中最外層的抽象宣告符具有尾隨返回型別,則只有當它以 auto 開頭時,歧義才會被解決為型別識別符號。

typedef struct BB { int C[2]; } *B, C;
 
void foo()
{
    sizeof(B()->C[1]);    // OK, sizeof(expression)
    sizeof(auto()->C[1]); // error: sizeof of a function returning an array
}
(C++11 起)

[編輯] 注意

功能測試宏 標準 特性
__cpp_auto_cast 202110L (C++23) auto(x)auto{x}

[編輯] 示例

#include <cassert>
#include <iostream>
 
double f = 3.14;
unsigned int n1 = (unsigned int)f; // C-style cast
unsigned int n2 = unsigned(f);     // function-style cast
 
class C1;
class C2;
C2* foo(C1* p)
{
    return (C2*)p; // casts incomplete type to incomplete type
}
 
void cpp23_decay_copy_demo()
{
    auto inc_print = [](int& x, const int& y)
    {
        ++x;
        std::cout << "x:" << x << ", y:" << y << '\n';
    };
 
    int p{1};
    inc_print(p, p); // prints x:2 y:2, because param y here is an alias of p
    int q{1};
    inc_print(q, auto{q}); // prints x:2 y:1, auto{q} (C++23) casts to prvalue,
                           // so the param y is a copy of q (not an alias of q)
}
 
// In this example, C-style cast is interpreted as static_cast
// even though it would work as reinterpret_cast
struct A {};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
 
int main()
{
    D* d = nullptr;
//  A* a = (A*)d;                   // compile-time error
    A* a = reinterpret_cast<A*>(d); // this compiles
    assert(a == nullptr);
 
    cpp23_decay_copy_demo();
}

輸出

x:2 y:2
x:2 y:1

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 1223
(P2915R0)
C++11 新增尾隨返回型別引入了更多歧義 解決了它們
CWG 1893 C++11 函式風格型別轉換未考慮包擴充套件 考慮了它們
CWG 2351 C++11 void{} 是格式錯誤的 使其格式正確
CWG 2620 C++98 歧義函式的解析
引數可能被誤解
改進了措辭
CWG 2828 C++98 如果 static_cast 後跟 const_cast 有多種解釋,則 C 風格型別轉換格式錯誤,
無論這些轉換是否實際使用
不管這些轉換是否實際使用
只考慮那些
轉換
可能被使用
CWG 2894 C++98 函式風格的型別轉換可以建立引用右值 只能建立引用左值

[編輯] 參考

  • C++23 標準 (ISO/IEC 14882:2024)
  • 7.6.1.4 顯式型別轉換(函式風格記法)[expr.type.conv]
  • 7.6.3 顯式型別轉換(C 風格記法)[expr.cast]
  • C++20 標準 (ISO/IEC 14882:2020)
  • 7.6.1.4 顯式型別轉換(函式風格記法)[expr.type.conv]
  • 7.6.3 顯式型別轉換(C 風格記法)[expr.cast]
  • C++17 標準 (ISO/IEC 14882:2017)
  • 8.2.3 顯式型別轉換(函式風格記法)[expr.type.conv]
  • 8.4 顯式型別轉換(C 風格記法)[expr.cast]
  • C++14 標準 (ISO/IEC 14882:2014)
  • 5.2.3 顯式型別轉換(函式風格記法)[expr.type.conv]
  • 5.4 顯式型別轉換(C 風格記法)[expr.cast]
  • C++11 標準 (ISO/IEC 14882:2011)
  • 5.2.3 顯式型別轉換(函式風格記法)[expr.type.conv]
  • 5.4 顯式型別轉換(C 風格記法)[expr.cast]
  • C++03 標準 (ISO/IEC 14882:2003)
  • 5.2.3 顯式型別轉換(函式風格記法)[expr.type.conv]
  • 5.4 顯式型別轉換(C 風格記法)[expr.cast]
  • C++98 標準 (ISO/IEC 14882:1998)
  • 5.2.3 顯式型別轉換(函式風格記法)[expr.type.conv]
  • 5.4 顯式型別轉換(C 風格記法)[expr.cast]

[編輯] 另請參閱

const_cast 轉換 新增或移除 const[編輯]
static_cast 轉換 執行基本轉換[編輯]
dynamic_cast 轉換 執行受檢多型轉換[編輯]
reinterpret_cast 轉換 執行通用低階轉換[編輯]
標準轉換 從一種型別到另一種型別的隱式轉換[編輯]
C 文件 中關於 轉換運算子 的內容