簡體   English   中英

將 make_shared 與 emplace_back 和 push_back 一起使用 - 有什么區別嗎?

[英]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_backemplace_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_backrvalue重載將通過調用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_backemplace_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)

  1. 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.

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