簡體   English   中英

減少臨時對象到現場施工的分配

[英]Reducing assignment of temporary object to in-place construction

以支持移動語義的std::list為例。

std::list<std::string> X;
... //X is used in various ways
X=std::list<std::string>({"foo","bar","dead","beef"});

自C ++ 11以來,編譯器執行分配的最直接方法是:

  1. 消滅X
  2. 構造std::list
  3. std::list移至X

現在,不允許編譯器執行以下操作:

  1. 消滅X
  2. 在原處構造std::list

因為雖然這顯然可以節省另一個memcpy但卻消除了分配。 使第二種行為成為可能和可用的便捷方法是什么? 在將來的C ++版本中計划了嗎?

我的猜測是,C ++仍然不提供以下功能:

X.~X();
new(&X) std::list<std::string>({"foo","bar","dead","beef"});

我對嗎?

實際上,您可以通過定義operator =來獲取初始化程序列表來完成此操作。 對於std :: list,只需調用

    X = {"foo","bar","dead","beef"}.

就您而言,實際上是:

  1. 建造一個臨時的
  2. 用臨時調用X上的移動分配運算符

在大多數對象上,例如std :: list,與僅構造一個對象相比,這實際上並不昂貴。

但是,它仍然會為第二個std :: list的內部存儲引起額外的分配,這可以避免:如果可能的話,我們可以重用已經為X分配的內部存儲。 正在發生的是:

  1. 構造:臨時元素為元素分配一些空間
  2. 移動:指針移動到X; 釋放X之前使用的空間

一些對象使賦值運算符超載以獲取初始化器列表, std :: vectorstd :: list就是這種情況。 這樣的操作員可以使用已經在內部分配的存儲,這是最有效的解決方案。

//請在此處插入有關過早優化的常見問題

在將來的C ++版本中計划了嗎?

不用了,謝謝你。

分配是一樣的破壞,然后創建。 X在您的分配示例中未銷毀。 X是有生命的物體; X內容可能會被破壞,但X本身永遠不會被破壞。 而且也不應該

如果您想銷毀X ,那么您就可以使用顯式析構和放置新函數來具有這種能力。 盡管感謝const成員的可能,但是如果您想確保安全,還需要清洗指向該對象的指針。 但是,分配永遠不應該被認為是等效的。

如果您關心效率,那么最好使用assign成員函數。 通過使用assignX可以重用現有分配。 而且這幾乎肯定會使它比您的“ destroy-plus-construct”版本更快。 將鏈接列表移動到另一個對象中的開銷很小。 不必為了重新分配而銷毀所有這些分配的成本。

這對於std::list尤其重要,因為它有很多分配。

在最壞的情況下, assign效率不會比您從課堂之外提出的其他效率低。 最好的情況是,情況會好得多。

當您有涉及移動分配的語句時:

x = std::move(y);

在執行移動之前,不會為x調用析構函數。 但是,在移動之后,有時將調用析構函數y 移動賦值運算符背后的想法是,它可能能夠以一種簡單的方式將y的內容移動到x (例如,將指向y的存儲的指針復制到x )。 它還必須確保其先前的內容被正確銷毀(它可能選擇與y交換它,因為您知道y可能不再被使用,並且y的析構函數將被調用)。

如果內聯移動分配,則編譯器可能能夠推斷出將存儲從y移至x所需的所有操作僅等同於就地構造。

再問你最后一個問題

”“對嗎?

沒有。

您關於允許或不允許的想法是錯誤的。 只要編譯器保留可觀察的效果,就可以替換任何優化。 這稱為“好像”規則。 如果不影響任何可見的代碼,則可能的優化包括刪除所有該代碼。 特別是,您對第二個示例的“不允許”完全是錯誤的,而“它消除分配”的理由也適用於您的第一個示例,在該示例中您得出相反的結論,即那里存在自相矛盾。

暫無
暫無

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

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