型別
物件、引用、函式(包括函式模板特化)和表示式都具有一個稱為**型別**的屬性,該屬性既限制了這些實體允許的操作,又為原本通用的位序列提供了語義含義。
目錄 |
[編輯] 型別分類
C++ 型別系統包含以下型別
- 基本型別(另請參閱 std::is_fundamental)
- 型別 void(另請參閱 std::is_void);
|
(C++11 起) |
- 算術型別(另請參閱 std::is_arithmetic)
- 整型型別(包括cv 限定版本,另請參閱 std::is_integral,整型型別的同義詞是整數型別)
- 型別 bool;
- 字元型別
- 窄字元型別
- 普通字元型別:char、signed char、unsigned char[1]
|
(C++20 起) |
- 寬字元型別:char16_t、char32_t,(C++11 起)wchar_t;
- 有符號整型型別
- 標準有符號整型型別:signed char、short、int、long、long long;
|
(C++11 起) |
- 無符號整型型別
- 標準無符號整型型別:unsigned char、unsigned short、unsigned、unsigned long、unsigned long long;
|
(C++11 起) |
- 浮點型別(另請參閱 std::is_floating_point)
- 標準浮點型別:float、double、long double 及其cv 限定版本;
(C++23 起) |
- 複合型別(另請參閱 std::is_compound)
- 引用型別(另請參閱 std::is_reference)
-
- 到物件型別的左值引用;
- 到函式型別的左值引用;
|
(C++11 起) |
- 指標型別(另請參閱 std::is_pointer)
- 成員指標型別(另請參閱 std::is_member_pointer)
- 資料成員指標型別(另請參閱 std::is_member_object_pointer);
- 成員函式指標型別(另請參閱 std::is_member_function_pointer);
- 陣列型別(另請參閱 std::is_array);
- 函式型別(另請參閱 std::is_function);
- 列舉型別(另請參閱 std::is_enum);
|
(C++11 起) |
- 類型別:
- 非聯合型別(另請參閱 std::is_class);
- 聯合型別(另請參閱 std::is_union)。
- ↑ signed char 和 unsigned char 是窄字元型別,但它們不是字元型別。換句話說,窄字元型別的集合不是字元型別集合的子集。
對於除引用和函式之外的每個非 cv 限定型別,型別系統支援該型別的三個額外的cv 限定版本(const、volatile 和 const volatile)。
[編輯] 其他類別
一個*物件型別*(另請參閱 std::is_object)是一個(可能 cv 限定的)型別,它不是函式型別、不是引用型別、也不是(可能 cv 限定的)void。
以下型別統稱為*標量型別*(另請參閱 std::is_scalar)
(C++11 起) |
- 這些型別的 cv 限定版本
以下型別統稱為*隱式生存期型別*
- 標量型別
- 隱式生存期類型別
- 陣列型別
- 這些型別的 cv 限定版本
以下型別統稱為*可平凡複製型別*
以下型別統稱為*標準佈局型別*
|
(C++11 起) |
型別特性層次結構圖 |
---|
注意:SVG 影像的元素是可點選的,但您必須首先在新瀏覽器選項卡中開啟該圖 |
[編輯] 已棄用類別
以下型別統稱為*POD 型別*(另請參閱 std::is_pod)
|
(C++20 中已棄用) |
以下型別統稱為*平凡型別*(另請參閱 std::is_trivial)
|
(C++11 起) (C++26 中已棄用) |
[編輯] 程式定義型別
一個*程式定義的特化*是一個顯式特化或部分特化,它不屬於 C++ 標準庫,也不是由實現定義的。
一個*程式定義型別*是以下型別之一
|
(C++11 起) |
- 一個程式定義的特化的例項化。
[編輯] 型別命名
可以透過以下方式宣告一個名稱來引用一個型別:
在 C++ 程式中,那些沒有名稱的型別常常需要被引用;其語法被稱為 type-id。命名型別 T
的 type-id 語法與宣告型別 T
的變數或函式的宣告語法完全相同,只是省略了識別符號,並且宣告語法的 decl-specifier-seq 被限制為 type-specifier-seq,而且只有當 type-id 出現在非模板類型別名宣告的右側時才能定義新型別。
int* p; // declaration of a pointer to int static_cast<int*>(p); // type-id is "int*" int a[3]; // declaration of an array of 3 int new int[3]; // type-id is "int[3]" (called new-type-id) int (*(*x[2])())[3]; // declaration of an array of 2 pointers to functions // returning pointer to array of 3 int new (int (*(*[2])())[3]); // type-id is "int (*(*[2])())[3]" void f(int); // declaration of a function taking int and returning void std::function<void(int)> x = f; // type template parameter is a type-id "void(int)" std::function<auto(int) -> void> y = f; // same std::vector<int> v; // declaration of a vector of int sizeof(std::vector<int>); // type-id is "std::vector<int>" struct { int x; } b; // creates a new type and declares an object b of that type sizeof(struct { int x; }); // error: cannot define new types in a sizeof expression using t = struct { int x; }; // creates a new type and declares t as an alias of that type sizeof(static int); // error: storage class specifiers not part of type-specifier-seq std::function<inline void(int)> f; // error: neither are function specifiers
宣告語法中移除了名稱的 declarator 部分被稱為 abstract-declarator。
Type-id 可用於以下情況
- 在轉換表示式中指定目標型別;
- 作為
sizeof
、alignof
、alignas
、new
和typeid
的引數; - 在類型別名宣告的右側;
- 作為函式宣告的尾隨返回型別;
- 作為模板型別引數的預設引數;
- 作為模板型別引數的模板引數;
|
(C++17 前) |
Type-id 可以在以下情況下進行一些修改後使用
- 在函式的引數列表中(當省略引數名稱時),type-id 使用 decl-specifier-seq 而不是 type-specifier-seq(特別是,允許某些儲存類說明符);
- 在使用者定義轉換函式的名稱中,抽象宣告符不能包含函式或陣列運算子。
本節不完整 原因:8.2[dcl.ambig.res] 如果可以簡潔地總結 |
本節不完整 原因:提及並連結到 decltype 和 auto |
[編輯] 詳細型別說明符
詳細型別說明符可用於引用先前宣告的類名(class、struct 或 union)或先前宣告的列舉名,即使該名稱被非型別宣告隱藏。它們也可用於宣告新的類名。
有關詳細資訊,請參閱詳細型別說明符。
[編輯] 靜態型別
透過程式編譯時分析得到的表示式的型別稱為表示式的*靜態型別*。靜態型別在程式執行期間不會改變。
[編輯] 動態型別
如果某個glvalue 表示式引用了多型物件,則其最派生物件的型別稱為動態型別。
// given struct B { virtual ~B() {} }; // polymorphic type struct D : B {}; // polymorphic type D d; // most-derived object B* ptr = &d; // the static type of (*ptr) is B // the dynamic type of (*ptr) is D
對於 prvalue 表示式,動態型別總是與靜態型別相同。
[編輯] 不完整型別
以下型別是*不完整型別*
所有其他型別都是完整型別。
以下任何上下文都要求型別 T
是完整型別
- 定義或呼叫返回型別為
T
或引數型別為T
的函式; - 定義型別為
T
的物件; - 宣告型別為
T
的非靜態類資料成員; - 為型別
T
的物件或元素型別為T
的陣列的new
表示式; - 應用於型別
T
的 glvalue 的左值到右值轉換; - 到型別
T
的隱式或顯式轉換; - 到型別 T* 或 T& 的標準轉換、
dynamic_cast
或static_cast
,除非是從空指標常量或指向可能 cv 限定 void 的指標轉換; - 應用於型別
T
的表示式的類成員訪問運算子; - 應用於型別
T
的typeid
、sizeof
或alignof
運算子; - 應用於指向
T
的指標的算術運算子; - 定義基類為
T
的類; - 賦值給型別為
T
的左值; - 型別為
T
、T& 或 T* 的處理程式。
(通常,當 T
的大小和佈局必須已知時。)
如果這些情況中的任何一種出現在翻譯單元中,則型別的定義必須出現在同一翻譯單元中。否則,則不是必需的。
不完整定義的物件型別可以完成
- 類型別(如 class X)在翻譯單元中的某一點可能被視為不完整,之後又被視為完整;型別 class X 在這兩個點是同一型別
struct X; // declaration of X, no definition provided yet extern X* xp; // xp is a pointer to an incomplete type: // the definition of X is not reachable void foo() { xp++; // ill-formed: X is incomplete } struct X { int i; }; // definition of X X x; // OK: the definition of X is reachable void bar() { xp = &x; // OK: type is “pointer to X” xp++; // OK: X is complete }
- 陣列物件的宣告型別可能是包含不完整類型別的陣列,因此是不完整的;如果該類型別隨後在翻譯單元中完成,則陣列型別變為完整型別;這兩個點的陣列型別是同一型別。
- 陣列物件的宣告型別可能是未知邊界的陣列,因此在翻譯單元中的某一點是不完整的,之後又變為完整的;這兩個點的陣列型別(“未知邊界的
T
陣列”和“N 個T
陣列”)是不同型別。
指向或引用未知邊界陣列的指標或引用型別永久指向或引用不完整型別。由 typedef
宣告命名的未知邊界陣列永久引用不完整型別。在這兩種情況下,陣列型別都不能完成
extern int arr[]; // the type of arr is incomplete typedef int UNKA[]; // UNKA is an incomplete type UNKA* arrp; // arrp is a pointer to an incomplete type UNKA** arrpp; void foo() { arrp++; // error: UNKA is an incomplete type arrpp++; // OK: sizeof UNKA* is known } int arr[10]; // now the type of arr is complete void bar() { arrp = &arr; // OK: qualification conversion (since C++20) arrp++; // error: UNKA cannot be completed }
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 328 | C++98 | 不完整型別的類成員未被禁止 如果從未建立類型別的物件 |
非靜態類資料成員 需要是完整型別 |
CWG 977 | C++98 | 列舉型別何時在其定義中變為完整型別 不明確 |
一旦基礎型別確定,該型別就 變為完整型別 |
CWG 1362 | C++98 | 到型別 T* 或 T& 的使用者定義轉換要求 T 為完整型別 |
未要求 |
CWG 2006 | C++98 | cv 限定的 void 型別是物件型別和完整型別 | 從這兩個類別中排除 |
CWG 2448 | C++98 | 只有 cv 非限定型別可以是整型和浮點型別 | 允許 cv 限定型別 |
CWG 2630 | C++98 | 不清楚一個類是否在其定義出現的翻譯單元之外被視為完整 如果其定義在這種情況下是可達的, |
則該類是完整型別。 如果其定義 在這種情況下是可達的 |
CWG 2643 | C++98 | 指向未知邊界陣列的指標型別 無法完成(但它已經是完整型別了) |
被指向的陣列型別 不能完成 |
LWG 2139 | C++98 | “使用者定義型別”的含義不明確 | 定義並使用“程式 定義型別”代替 |
LWG 3119 | C++11 | 不清楚閉包型別是否是程式定義型別 | 已明確 |
[編輯] 參考文獻
- C++23 標準 (ISO/IEC 14882:2024)
- 6.8.2 基本型別 [basic.fundamental]
- C++20 標準 (ISO/IEC 14882:2020)
- 6.8.2 基本型別 [basic.fundamental]
- C++17 標準 (ISO/IEC 14882:2017)
- 6.9.1 基本型別 [basic.fundamental]
- C++14 標準 (ISO/IEC 14882:2014)
- 3.9.1 基本型別 [basic.fundamental]
- C++11 標準 (ISO/IEC 14882:2011)
- 3.9.1 基本型別 [basic.fundamental]
- C++98 標準 (ISO/IEC 14882:1998)
- 3.9.1 基本型別 [basic.fundamental]
[編輯] 另請參閱
型別特性 | 用於查詢型別屬性的編譯時基於模板的介面 |
C 文件 用於 Type
|
[編輯] 外部連結
1. | Howard Hinnant 的 C++0x 型別樹 |