[英]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以來,編譯器執行分配的最直接方法是:
X
std::list
std::list
移至X
現在,不允許編譯器執行以下操作:
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"}.
就您而言,實際上是:
在大多數對象上,例如std :: list,與僅構造一個對象相比,這實際上並不昂貴。
但是,它仍然會為第二個std :: list的內部存儲引起額外的分配,這可以避免:如果可能的話,我們可以重用已經為X分配的內部存儲。 正在發生的是:
一些對象使賦值運算符超載以獲取初始化器列表, std :: vector和std :: list就是這種情況。 這樣的操作員可以使用已經在內部分配的存儲,這是最有效的解決方案。
//請在此處插入有關過早優化的常見問題
在將來的C ++版本中計划了嗎?
不用了,謝謝你。
分配是不一樣的破壞,然后創建。 X
在您的分配示例中未銷毀。 X
是有生命的物體; X
的內容可能會被破壞,但X
本身永遠不會被破壞。 而且也不應該 。
如果您想銷毀X
,那么您就可以使用顯式析構和放置新函數來具有這種能力。 盡管感謝const
成員的可能,但是如果您想確保安全,還需要清洗指向該對象的指針。 但是,分配永遠不應該被認為是等效的。
如果您關心效率,那么最好使用assign
成員函數。 通過使用assign
, X
可以重用現有分配。 而且這幾乎肯定會使它比您的“ 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.