[英]Using make_shared with emplace_back and push_back - any difference?
some_vector.push_back(make_shared<ClassName>());
some_vector.emplace_back(make_shared<ClassName>());
我想檢查我的理解是否正確,對於make_shared
以及通常對於返回 object 的所有其他函數,這兩個調用是相同的。 這里make_shared
將創建一個新的shared_ptr
,然后這個指針將被移動到push_back
和emplace_back
的容器中。 這是正確的,還是會有一些不同?
vector<T>::push_back
有一個T&&
重載,它與vector<T>::emplace_back
T&&
版本相同。
不同之處在於emplace_back
會將任何一組 arguments 完美轉發到T
的構造函數,而push_back
只接受T&&
或T const&
。 當您實際傳遞T&&
或T const&
時,它們行為的標准規范是相同的。
我想在 Yakk 的回答中添加一個小細節。
arguments 的emplace_back
-case 的轉發可能會引入令人懷疑的可怕錯誤 - 即使對於共享指針的向量 - 如果不特別小心使用,請參閱例如
#include <vector>
#include <memory>
struct SimpleStruct {};
auto main() -> int
{
std::vector<std::shared_ptr<SimpleStruct>> v;
SimpleStruct a;
v.emplace_back(std::addressof(a)); // compiles, UB
v.push_back(std::addressof(a)); // fails to compile
}
是的,這是一個極端的例子,因為像這樣的代碼應該始終特別小心地使用或通常受到質疑,但它強調,如果一個人沒有復制 object 已經在手邊,那么它應該只引用emplace_back
唯一的目的是添加到向量中,並參考push_back
以了解所有常見的復制/移動構造情況。 如果語言/標准庫可以從頭開始強制emplace_back
,那就太好了,即只接受自定義的非復制/移動構造函數以獲得這種清晰的分離,但即使它可能以可接受的方式,它也會在與許多模板上下文場景(快速轉發)沖突並且容易出錯的用法仍然是可能的,盡管更明確一點。
根據我上面的例子,代碼重構是一個值得懷疑的重點。 簡單地想象一下前面的代碼使用了原始指針,即實際的底層錯誤已經在那里持續存在並且被emplace_back
隱藏了。 它也會被push_back
-usage 隱藏在那里,但不會在您將代碼更新為共享指針方式時立即隱藏。
即使它與您的特定用例無關,我認為這里值得一提,因為人們應該對兩種方法之間的潛在差異完全有信心。
感謝 Human-Compiler 在評論中提到我之前使用的錯誤術語。
為了理解這個問題,讓我們首先考慮調用std::make_shared<class_type>()
的結果,
它返回臨時的 object ,這意味着Xvalue
是一個eXpiring
值,其資源可以重復使用。 現在讓我們看看這兩種情況,
some_vector.push_back(make_shared<ClassName>());
std::vector
有兩個push_back
重載,其中一個接受rvalue
引用,即constexpr void push_back( T&& value );
這意味着value
被移動到新元素中,但是如何? push_back
的rvalue
重載將通過調用shared_ptr( shared_ptr&& r ) noexcept;
r
的所有權將被占用, r
將變為空。
some_vector.emplace_back(make_shared<ClassName>());
在emplace_back( Args&&... args )
元素是通過std::allocator_traits::construct
通過完美轉發args..
通過std::forward<Args>(args)...
構造的,這意味着rvalue
將完美轉發並導致相同移動構造函數shared_ptr( shared_ptr&& r ) noexcept;
被調用。
結論是, push_back
和emplace_back
都具有相同的效果。
但是上面解釋的事情並沒有發生,因為compiler
進入圖片並且它做了什么,它執行優化,這意味着它不是創建臨時對象並將它們移動到其他對象中,而是直接在適當的位置創建對象。
同樣,兩種情況下的結果都是相同的。
下面,包含編譯器優化理論的支持代碼,如您所見,output 僅打印一個構造函數調用。
#include <iostream>
using std::cout;
using std::endl;
class Object{
public:
explicit Object(int );
Object(const Object& );
Object(Object&& );
};
Object::Object(int ){
cout<< __PRETTY_FUNCTION__<< endl;
}
Object::Object(const Object& ){
cout<< __PRETTY_FUNCTION__<< endl;
}
Object::Object(Object&& ){
cout<< __PRETTY_FUNCTION__<< endl;
}
int main(){
[[maybe_unused]] Object obj(Object(1));
}
Output:
Object::Object(int)
some_vector.push_back(make_shared<ClassName>());
右值引用傳遞給 function,push_back 只是調用 emplace_back。
void push_back(value_type&& __x) { emplace_back(std::move(__x)); }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.