簡體   English   中英

為什么 std::atomic 的默認構造函數不默認初始化底層存儲值?

[英]Why does default constructor of std::atomic not default initialize the underlying stored value?

由於今天是美國的感恩節,我將成為指定的火雞來問這個問題:

采取像這樣無害的事情。 具有簡單普通舊數據類型(例如 int)的原子:

atomic<int> x;
cout << x;

以上將打印出垃圾(未定義)數據。 鑒於我閱讀的原子構造函數,這是有道理的:

(1) 默認構造函數

使原子對象處於未初始化狀態。 未初始化的原子對象稍后可以通過調用 atomic_init 進行初始化。

感覺像是一個奇怪的委員會決定。 但我相信他們有他們的理由。 但我想不出另一個std::類,其中默認構造函數會使對象處於未定義狀態。

我可以看到對於沒有默認構造函數並且需要進入atomic_init路徑的更復雜類型與std::atomic一起使用有何意義。 但更一般的情況是使用具有簡單類型的原子,用於引用計數、順序標識符值和簡單的基於輪詢的鎖定等場景。 因此,這些類型沒有自己的存儲值“零初始化”(默認初始化)感覺很奇怪。 或者至少,如果不可預測,為什么要有一個默認構造函數。

未初始化的std::atomic實例會有用的原因是什么。

P0883中所述,此行為的主要原因是與 C 的兼容性。顯然 C 沒有值初始化的概念; atomic_int i; 不執行初始化。 為了與 C 兼容,等效的 C++ 也必須不執行初始化。 由於 C++ 中的atomic_int應該是std::atomic<int>的別名,因此為了完全兼容 C/C++,該類型也必須不執行初始化。

幸運的是, C++20 似乎正在消除這種行為

未初始化的std::atomic實例會有用的原因是什么。

出於同樣的原因,基本的“構建塊”用戶定義類型不應做超出嚴格要求的事情,尤其是在像構造這樣不可避免的操作中。

但我想不出另一個 std:: 類,其中默認構造函數會使對象處於未定義狀態。

所有不需要內部不變量的類都是這種情況。

通用代碼中沒有期望T x; 將創建一個零初始化對象 但預計它將創建一個可用狀態的對象。 對於標量類型,任何現有對象在其生命周期內都是可用的。

另一方面,預計

T x = T();

將為通用代碼創建一個處於默認狀態的對象,用於普通值類型。 (如果所表示的值有這樣的東西,它通常是一個“零值”。)

原子非常不同,它們存在於不同的“世界”

原子並不是真正關於值的范圍。 它們旨在為讀取、寫入和復雜操作提供特殊保證; 原子在很多方面不同於其他數據類型,因為沒有根據對該對象的正常賦值定義復合賦值操作。 所以通常的等價物不適用於原子。 您不能像對普通對象那樣對原子進行推理。

您根本無法在原子和普通對象上編寫通用代碼; 這將毫無意義。

(見腳注。)

概括

  • 您可以擁有通用代碼,但不能擁有原子-非原子通用算法,因為它們的語義不屬於同一風格的語義定義(甚至不清楚 C++ 如何同時具有原子和非原子操作)。
  • “你不用為你不用的東西付錢。”
  • 沒有通用代碼會假定未初始化的變量具有值; 只是它處於賦值和其他不依賴於先前值的操作的有效狀態(顯然沒有復合賦值)。
  • 許多 STL 類型沒有被它們的默認構造函數初始化為“零”或默認值。

[腳注:

下面是一篇技術性重要的文字“吐槽”,但對於理解原子對象的構造函數為什么會是這樣並不重要。

它們只是以最深刻的方式遵循不同的語義規則:標准甚至沒有描述的方式,因為標准從未解釋多線程的最基本事實:語言的某些部分被評估為一系列操作取得進展,而其他領域(原子,try_lock ...)則沒有。 事實上,標准的作者顯然甚至沒有看到這種區別,甚至不理解這種二元性 (請注意,討論這些問題通常會使您的問題和答案遭到否決和刪除。)

這種區別是必不可少的,因為沒有它(同樣,它在標准中沒有出現),零個程序甚至可以具有多線程定義的行為:只有舊式的線程前行為可以在沒有這種二元性的情況下得到解釋。

C++ 委員會不了解 C++ 的症狀是他們認為“無稀有值”是一個額外的特性,而不是語義的重要組成部分(沒有獲得原子的“無稀有”保證承諾順序程序的順序語義更站不住腳)。

--尾注]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM