儲存類說明符
儲存類說明符是名稱宣告語法的decl-specifier-seq的一部分。它們與名稱的作用域一起,控制名稱的兩個獨立屬性:其儲存期和其連結。
目錄 |
[編輯] 儲存期
儲存期是物件的屬性,它定義了包含物件的儲存的最小潛在生命週期。儲存期由用於建立物件的構造確定,並且是以下之一:
- 靜態儲存期
|
(C++11 起) |
- 自動儲存期
- 動態儲存期
靜態、執行緒(C++11 起)和自動儲存期與透過宣告引入的物件以及臨時物件相關聯。動態儲存期與透過new 表示式建立的物件或隱式建立的物件相關聯。
儲存期類別也適用於引用。
子物件和引用成員的儲存期是其完整物件的儲存期。
[編輯] 說明符
以下關鍵字是儲存類說明符
|
(C++11 前) |
|
(C++17 前) |
- static
|
(C++11 起) |
- extern
- mutable
在decl-specifier-seq中,最多隻能有一個儲存類說明符,但thread_local可以與static或extern一起出現(C++11 起)。
mutable對儲存期沒有影響。有關其用法,請參見const/volatile。
其他儲存類說明符可以出現在以下宣告的decl-specifier-seq中
說明符 | 可出現在以下宣告的decl-specifier-seq中 | ||||||||
---|---|---|---|---|---|---|---|---|---|
變數宣告 | 函式宣告 | 結構化繫結宣告 (C++17 起) | |||||||
非成員 | 成員 | 非成員 | 成員 | ||||||
非引數 | 函式引數 | 非靜態 | 靜態 | 非靜態 | 靜態 | ||||
auto | 僅限塊作用域 | 是 | 否 | 否 | 否 | 否 | 否 | 不適用 | |
register | 僅限塊作用域 | 是 | 否 | 否 | 否 | 否 | 否 | 不適用 | |
static | 是 | 否 | 宣告為靜態 | 僅限名稱空間作用域 | 宣告為靜態 | 是 | |||
thread_local | 是 | 否 | 否 | 是 | 否 | 否 | 否 | 是 | |
extern | 是 | 否 | 否 | 否 | 是 | 否 | 否 | 否 |
匿名聯合也可以用static宣告。
register是一個提示,表示所宣告的變數將被大量使用,因此其值可以儲存在CPU暫存器中。該提示可以被忽略,在大多數實現中,如果變數的地址被獲取,它將被忽略。此用法已棄用。 |
(C++17 前) |
[編輯] 靜態儲存期
滿足以下所有條件的變數具有靜態儲存期
- 它屬於名稱空間作用域,或者首次用static或extern宣告。
|
(C++11 起) |
這些實體的儲存期與程式執行期間的持續時間相同。
執行緒儲存期所有用thread_local宣告的變數都具有執行緒儲存期。 這些實體的儲存期與它們被建立的執行緒的持續時間相同。每個執行緒都有一個獨立的物件或引用,並且宣告的名稱的使用是指與當前執行緒相關聯的實體。 |
(C++11 起) |
[編輯] 自動儲存期
以下變數具有自動儲存期
- 屬於塊作用域且未顯式宣告為static、thread_local(C++11 起)或extern的變數。此類變數的儲存期持續到它們被建立的塊退出。
- 屬於引數作用域(即函式引數)的變數。函式引數的儲存期持續到其銷燬之後。
[編輯] 動態儲存期
在程式執行期間透過以下方法建立的物件具有動態儲存期
[編輯] 連結
名稱可以具有外部連結、模組連結(C++20 起)、內部連結或無連結
|
(C++20 起) |
- 名稱具有內部連結的實體可以在同一翻譯單元中的另一個作用域中重新宣告。
- 名稱具有無連結的實體只能在同一作用域中重新宣告。
識別出以下連結
[編輯] 無連結
以下在塊作用域中宣告的任何名稱都具有無連結
- 未顯式宣告為extern的變數(無論是否有static修飾符);
- 區域性類及其成員函式;
- 在塊作用域中宣告的其他名稱,如typedef、列舉和列舉器。
未指定外部、模組(C++20 起)或內部連結的名稱也具有無連結,無論它們在哪個作用域中宣告。
[編輯] 內部連結
以下在名稱空間作用域中宣告的任何名稱都具有內部連結
- 用static宣告的變數、變數模板(C++14 起)、函式或函式模板;
- 非模板(C++14 起)非 volatile const 限定型別的變數,除非
|
(C++17 起) |
(C++20 起) |
- 它們顯式宣告為extern,或者
- 它們之前已宣告,且之前的宣告沒有內部連結;
- 匿名聯合的資料成員。
此外,在未命名名稱空間或未命名名稱空間內的名稱空間中宣告的所有名稱,即使是顯式宣告為extern的名稱,也具有內部連結。 |
(C++11 起) |
[編輯] 外部連結
具有外部連結的變數和函式也具有語言連結,這使得連結用不同程式語言編寫的翻譯單元成為可能。
以下在名稱空間作用域中宣告的任何名稱都具有外部連結,除非它們在未命名名稱空間中宣告或其宣告附加到命名模組且未匯出(C++20 起)
- 未在上面列出的變數和函式(即未宣告static的函式、未宣告static的非const變數,以及任何宣告為extern的變數);
- 列舉;
- 類名、其成員函式、靜態資料成員(無論是否const)、巢狀類和列舉,以及在類體內首次透過friend宣告引入的函式;
- 未在上面列出的所有模板名稱(即未宣告static的函式模板)。
以下在塊作用域中首次宣告的任何名稱都具有外部連結
- 宣告為extern的變數名稱;
- 函式名稱。
模組連結在名稱空間作用域中宣告的名稱如果其宣告附加到命名模組且未匯出,並且不具有內部連結,則具有模組連結。 |
(C++20 起) |
本節不完整 理由:新增在同一翻譯單元中宣告具有不同連結的實體時的行為描述(6.6 段落 6),注意 C++20(格式錯誤)與當前草案(格式良好)之間的區別 |
[編輯] 靜態塊變數
具有靜態或執行緒(C++11 起)儲存期的塊變數在控制流首次透過其宣告時初始化(除非其初始化是零初始化或常量初始化,這可以在首次進入塊之前執行)。在所有後續呼叫中,宣告被跳過。
- 如果初始化丟擲異常,則變數不被視為已初始化,並且在控制流下次透過宣告時將再次嘗試初始化。
- 如果初始化遞迴地進入正在初始化變數的塊,則行為未定義。
|
(C++11 起) |
具有靜態儲存期的塊變數的解構函式在程式退出時呼叫,但僅當初始化成功時。
在同一個行內函數(可能隱式內聯)的所有定義中具有靜態儲存期的變數都引用在一個翻譯單元中定義的同一個物件,只要該函式具有外部連結。
[編輯] 翻譯單元區域性實體
翻譯單元區域性實體的概念在 C++20 中標準化,更多詳細資訊請參見此頁面。
如果滿足以下條件,則實體是翻譯單元區域性的(簡稱TU-local):
- 它具有內部連結的名稱,或者
- 它沒有連結的名稱,並且是在 TU 區域性實體的定義中引入的,或者
- 它是一個模板或模板特化,其模板實參或模板宣告使用 TU 區域性實體。
如果非 TU 區域性實體的型別依賴於 TU 區域性實體,或者非 TU 區域性實體的宣告,或其推導指引(C++17 起)在以下之外命名了 TU 區域性實體,則可能會發生不良情況(通常是違反ODR):
- 非行內函數或函式模板的函式體
- 變數或變數模板的初始化器
- 類定義中的友元宣告
- 變數值的使用,如果該變數可在常量表達式中使用
此類用法在模組介面單元中(如果存在私有模組片段,則在其外部)或模組分割槽中是不允許的,並且在任何其他上下文中都已棄用。 在一個翻譯單元中出現的宣告不能命名在另一個非頭單元的翻譯單元中宣告的 TU 區域性實體。為模板例項化的宣告出現在特化的例項化點。 |
(C++20 起) |
[編輯] 注意
C 中頂層名稱空間作用域(檔案作用域)中const且非extern的名稱具有外部連結,但在 C++ 中具有內部連結。
自 C++11 起,auto不再是儲存類說明符;它用於指示型別推導。
在 C 中,不能獲取register變數的地址,但在 C++ 中,宣告為register的變數在語義上與未宣告任何儲存類說明符的變數無法區分。 |
(C++17 前) |
在 C++ 中,與 C 不同,變數不能宣告為register。 |
(C++17 起) |
具有內部或外部連結的thread_local變數的名稱,從不同作用域引用時,可能引用相同或不同的例項,具體取決於程式碼是在相同執行緒中還是在不同執行緒中執行。
extern關鍵字也可以用於指定語言連結和顯式模板例項化宣告,但在這些情況下它不是儲存類說明符(除非宣告直接包含在語言連結規範中,在這種情況下,宣告被視為包含extern說明符)。
儲存類說明符,除了thread_local,不允許用於顯式特化和顯式例項化
template<class T> struct S { thread_local static int tlm; }; template<> thread_local int S<float>::tlm = 0; // "static" does not appear here
const(可能由constexpr隱式指定)變數模板預設具有內部連結,這與其他模板實體不一致。缺陷報告CWG2387糾正了這一點。 |
(C++14 起) |
inline 透過預設賦予外部連結來作為CWG2387的變通方法。這就是為什麼在許多變數模板中新增了inline,然後在接受 CWG2387 後又刪除了它。只要支援的編譯器尚未實現 CWG2387,標準庫實現也需要使用inline。請參閱GCC Bugzilla #109126和MSVC STL PR #4546。 |
(C++17 起) |
功能測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_threadsafe_static_init |
200806L |
(C++11) | 併發時的動態初始化和銷燬 |
[編輯] 關鍵字
auto, register, static, extern, thread_local, mutable
[編輯] 示例
#include <iostream> #include <mutex> #include <string> #include <thread> thread_local unsigned int rage = 1; std::mutex cout_mutex; void increase_rage(const std::string& thread_name) { ++rage; // modifying outside a lock is okay; this is a thread-local variable std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for " << thread_name << ": " << rage << '\n'; } int main() { std::thread a(increase_rage, "a"), b(increase_rage, "b"); { std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for main: " << rage << '\n'; } a.join(); b.join(); }
可能的輸出
Rage counter for a: 2 Rage counter for main: 1 Rage counter for b: 2
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 216 | C++98 | 類作用域中的未命名類和列舉具有 與名稱空間作用域中不同的連結 |
它們都具有外部連結 在這些作用域中 |
CWG 389 | C++98 | 無連結的名稱不應 用於宣告具有連結的實體 |
無連結的型別不應被用作 具有連結的變數或函式的型別 除非該變數或函式具有 C 語言連結 或函式具有 C 語言連結 |
CWG 426 | C++98 | 一個實體可以在同一翻譯單元中 同時宣告為內部和外部連結 |
在這種情況下程式格式錯誤 |
CWG 527 | C++98 | CWG 389 決議引入的型別限制 也適用於不能在其自身翻譯單元之外 命名的變數和函式 |
對於這些變數和函式(即無 連結或內部連結,或在未命名 名稱空間內宣告的)取消了限制 名稱空間內宣告的)取消了限制 |
CWG 809 | C++98 | register作用很小 | 已棄用 |
CWG 1648 | C++11 | static被隱式指定,即使 thread_local與extern結合使用 |
僅當沒有其他儲存 類說明符存在時才隱式指定 |
CWG 1686 | C++98 C++11 |
在名稱空間作用域中宣告的非靜態變數的名稱 只有在其顯式宣告為const(C++98)或constexpr(C++11)時才具有內部連結 只有在其顯式宣告為const(C++98)或constexpr(C++11)時才具有內部連結 |
僅要求型別為 const-qualified 為 const-qualified |
CWG 2019 | C++98 | 引用成員的儲存期 未指定 |
與其完整物件相同 |
CWG 2387 | C++14 | 不清楚 const 限定變數模板是否 預設具有內部連結 |
const 限定符不影響 變數模板或其例項的連結 模板或其例項的連結 |
CWG 2533 | C++98 | 隱式建立物件的儲存期 不清楚 |
已明確 |
CWG 2850 | C++98 | 不清楚函式引數的儲存何時 被釋放 |
已明確 |
CWG 2872 | C++98 | “可以被引用”的含義不清楚 | 改進措辭 |
P2788R0 | C++20 | 在模組單元中宣告 const 限定變數 在名稱空間中賦予其內部連結 |
不賦予內部連結 |
[編輯] 參考
- C++23 標準 (ISO/IEC 14882:2024)
- 6.7.5 儲存期 [basic.stc]
- C++20 標準 (ISO/IEC 14882:2020)
- 6.7.5 儲存期 [basic.stc]
- C++17 標準 (ISO/IEC 14882:2017)
- 6.7 儲存期 [basic.stc]
- C++14 標準 (ISO/IEC 14882:2014)
- 3.7 儲存期 [basic.stc]
- C++11 標準 (ISO/IEC 14882:2011)
- 3.7 儲存期 [basic.stc]
- C++03 標準 (ISO/IEC 14882:2003)
- 3.7 儲存期 [basic.stc]
- C++98 標準 (ISO/IEC 14882:1998)
- 3.7 儲存期 [basic.stc]
[編輯] 參見
C 文件有關儲存期
|