宣告
宣告 (Declarations) 是將名稱引入(或重新引入)C++ 程式的方式。並非所有的宣告都能宣告具體事物,且每種類型的實體(entity)宣告方式皆有所不同。定義 (Definitions) 是足以用來使用該名稱所標識實體的宣告。
宣告包含以下幾種:
- 函式定義
- 範本宣告 (Template declaration)(包含部分範本特化)
- 顯式範本實例化 (Explicit template instantiation)
- 顯式模板特化
- 命名空間定義
- 連結規範 (Linkage specification)
|
(C++11 起) |
- 空宣告 (Empty declaration) (
;) - 不含 decl-specifier-seq 的函式宣告
attr (optional) declarator ; |
|||||||||
| 屬性 | - | (since C++11) 任意數量的 屬性序列 |
| 宣告子 (declarator) | - | 一個函式宣告子 (function declarator) |
- 此宣告必須宣告建構函式、解構函式或使用者自定義型別的轉型函式 (conversion function)。它只能作為範本宣告、顯式特化或顯式實例化的一部分使用。
- 區塊宣告 (block-declaration)(可出現於區塊內的宣告),其本身可以是下列其中之一:
| (C++11 起) |
| (自 C++20 起) | |
| (C++11 起) |
- 簡單宣告 (simple declaration)
目錄 |
[edit] 簡單宣告
簡單宣告是一種引入、建立並可選擇性初始化一個或多個識別碼(通常為變數)的語句。
decl-specifier-seq init-declarator-list (optional) ; |
(1) | ||||||||
attr decl-specifier-seq init-declarator-list ; |
(2) | (C++11 起) | |||||||
| decl-specifier-seq (宣告說明符序列) | - | 說明符序列 |
| 初始化宣告子列表 (init-declarator-list) | - | 以逗號分隔的 init-declarator 列表(詳見下文) |
| 屬性 | - | 任意數量的屬性序列 |
init-declarator-list 僅在宣告具名類別或具名列舉時可省略。
| (自 C++17 起) |
init-declarator 的語法定義如下:
| declarator initializer | (1) | ||||||||
| declarator requires-clause (optional) contract-specs (optional) | (2) | ||||||||
| 宣告子 (declarator) | - | 一個 宣告子 |
| 初始化式 (initializer) | - | 一個 初始化式 |
| requires-clause | - | (since C++20) 一個 requires 子句 |
| contract-specs | - | (since C++26) 一個 函式合約說明符 (function contract specifiers) 列表 |
|
requires-clause 僅當 declarator 宣告的是一個範本化函式 (templated function) 時才可出現。 |
(自 C++20 起) |
|
contract-specs 僅當 declarator 宣告的是一個函式或函式範本時才可出現。 |
(C++26 起) |
[edit] 說明符
宣告說明符 (Declaration specifiers) (decl-specifier-seq) 是下列以空白分隔的說明符序列,順序不拘:
typedef說明符。若存在,整個宣告即為 typedef 宣告,每個宣告子都會引入一個新的型別名稱,而非物件或函式。- 函式說明符 (
inline,virtual,explicit),僅允許用於函式宣告。
|
(自 C++17 起) |
friend說明符,允許用於類別和函式宣告。
|
(C++11 起) |
| (自 C++20 起) |
- 儲存類別說明符 (storage class specifier) (register, (until C++17) static, thread_local, (since C++11) extern, mutable)。僅允許一個儲存類別說明符,例外情況為 thread_local 可與 extern 或 static 同時出現(since C++11)。
- 型別說明符 (Type specifiers) (type-specifier-seq),是一個命名型別的說明符序列。該宣告所引入的每個實體,其型別均為此型別,並可由宣告子選擇性地修改(見下文)。此說明符序列亦由 type-id 使用。只有下列說明符屬於 type-specifier-seq,順序不拘:
- 類別說明符 (class specifier)
- 列舉說明符 (enum specifier)
- 簡單型別說明符 (simple type specifier)
| (C++11 起) | |
| (C++26 起) |
- 先前宣告過的類別名稱(可選限定)
- 先前宣告過的列舉名稱(可選限定)
- 先前宣告過的 typedef-名稱 或 型別別名(since C++11)(可選限定)
- 帶有範本引數的範本名稱(可選限定,可選使用 template 消歧義器)
| (自 C++17 起) |
-
- 在 decl-specifier-seq 中僅允許出現一個型別說明符,但以下情況除外:
- const 可與任何其他型別說明符組合(自身除外)。
- volatile 可與任何其他型別說明符組合(自身除外)。
- signed 或 unsigned 可與 char, long, short 或 int 組合。
- short 或 long 可與 int 組合。
- long 可與 double 組合。
|
(C++11 起) |
屬性可出現在 decl-specifier-seq 中,在此情況下,它們適用於由前面說明符所決定的型別。
在 decl-specifier-seq 中重複使用任何說明符(例如 const static const 或 virtual inline virtual)均為錯誤,例外情況為 long 允許出現兩次(since C++11)。
[edit] 宣告子 (Declarators)
init-declarator-list 中的每個 init-declarator(形式為 S D1, D2, D3;)的處理方式,皆如同將其視為帶有相同說明符的獨立宣告:S D1; S D2; S D3;。
每個宣告子引入恰好一個物件、參考、函式或(對於 typedef 宣告)型別別名,其型別由 decl-specifier-seq 提供,並可選由宣告子中的運算子(如 &(參考至)、[](陣列)或 ()(函式回傳值)進行修改。這些運算子可遞迴應用,如下所示。
宣告子 (declarator) 為以下其中之一:
| unqualified-id attr (optional) | (1) | ||||||||
| qualified-id attr (optional) | (2) | ||||||||
... identifier attr (optional) |
(3) | (C++11 起) | |||||||
* attr (optional) cv (optional) declarator |
(4) | ||||||||
nested-name-specifier * attr (optional) cv (optional) declarator |
(5) | ||||||||
& attr (optional) declarator |
(6) | ||||||||
&& attr (optional) declarator |
(7) | (C++11 起) | |||||||
noptr-declarator [ constexpr (optional) ] attr (optional) |
(8) | ||||||||
noptr-declarator ( parameter-list ) cv (optional) ref (optional) except (optional) attr (optional) |
(9) | ||||||||
D 宣告為指向 C 的成員指標,該成員型別由 decl-specifier-seq S 決定。nested-name-specifier 是名稱與範疇解析運算子 :: 的序列。|
在所有情況下,attr 皆為選用的屬性序列。當其緊接在識別碼後出現時,它適用於正在被宣告的物件。 |
(C++11 起) |
cv 是 const 和 volatile 限定詞的序列,其中每個限定詞在序列中至多可出現一次。
| 本節尚不完整 原因:解釋宣告名稱隱藏規則;變數/函式宣告如何隱藏具有相同名稱的類別(但不會隱藏 typedef)。 |
[edit] 附註
當 區塊宣告出現在區塊內,且由宣告引入的識別碼先前已在外層區塊中宣告,則外層宣告會在該區塊剩餘部分中被隱藏。
如果一個宣告引入了具有自動儲存期的變數,則該變數會在執行其宣告語句時進行初始化。所有在區塊內宣告的自動變數都會在離開該區塊時被銷毀(無論離開方式為何:透過例外、goto,或是到達區塊結尾),銷毀順序與其初始化順序相反。
[edit] 範例
注意:此範例示範了一些複雜宣告如何根據語言語法進行解析。其他常見的記憶法包括:螺旋規則 (the spiral rule)、由內而外讀取,以及宣告反映用法 (declaration mirrors use)。此外,網址 https://cdecl.org 提供了一個自動化解析器。
#include <type_traits> struct S { int member; // decl-specifier-seq is "int" // declarator is "member" } obj, *pObj(&obj); // decl-specifier-seq is "struct S { int member; }" // declarator "obj" declares an object of type S // declarator "*pObj" declares a pointer to S, // and initializer "(&obj)" initializes it int i = 1, *p = nullptr, f(), (*pf)(double); // decl-specifier-seq is "int" // declarator "i" declares a variable of type int, // and initializer "= 1" initializes it // declarator "*p" declares a variable of type int*, // and initializer "= nullptr" initializes it // declarator "f()" declares (but doesn't define) // a function taking no arguments and returning int // declarator "(*pf)(double)" declares a pointer to function // taking double and returning int int (*(*var1)(double))[3] = nullptr; // decl-specifier-seq is "int" // declarator is "(*(*var1)(double))[3]" // initializer is "= nullptr" // 1. declarator "(*(*var1)(double))[3]" is an array declarator: // Type declared is: "(*(*var1)(double))" array of 3 elements // 2. declarator "(*(*var1)(double))" is a pointer declarator: // Type declared is: "(*var1)(double)" pointer to array of 3 elements // 3. declarator "(*var1)(double)" is a function declarator: // Type declared is: "(*var1)" function taking "(double)", // returning pointer to array of 3 elements. // 4. declarator "(*var1)" is a pointer declarator: // Type declared is: "var1" pointer to function taking "(double)", // returning pointer to array of 3 elements. // 5. declarator "var1" is an identifier. // This declaration declares the object var1 of type "pointer to function // taking double and returning pointer to array of 3 elements of type int" // The initializer "= nullptr" provides the initial value of this pointer. // C++11 alternative syntax: auto (*var2)(double) -> int (*)[3] = nullptr; // decl-specifier-seq is "auto" // declarator is "(*var2)(double) -> int (*)[3]" // initializer is "= nullptr" // 1. declarator "(*var2)(double) -> int (*)[3]" is a function declarator: // Type declared is: "(*var2)" function taking "(double)", returning "int (*)[3]" // ... int main() { static_assert(std::is_same_v<decltype(var1), decltype(var2)>); }
[edit] 缺陷報告
下列更改行為的缺陷報告追溯應用於之前的 C++ 標準。
| DR | 應用於 | 出版時的行為 | 正確的行為 |
|---|---|---|---|
| CWG 482 | C++98 | 重新宣告的宣告子無法使用限定 (qualified) | 允許使用限定宣告子 |
| CWG 569 | C++98 | 單獨的分號不是有效的宣告 | 它被定義為空宣告, 沒有任何副作用 |
| CWG 1830 | C++98 | 在 decl-specifier-seq 中重複函式說明符是被允許的 | 禁止重複 |
[edit] 參見
| C 語言文件 關於 宣告
|