2014-02-27 83 views

回答

14

我構成,即實際失效時push_backemplace_back取代編譯一個短例如:

#include <vector> 
struct S { 
    S(double) {} 
    private: 
    explicit S(int) {} 
}; 
int main() { 
    std::vector<S>().push_back(0); // OK 
    std::vector<S>().emplace_back(0); // error! 
} 

push_back的呼叫需要它的參數從0int類型轉換爲鍵入S。由於這是隱式轉換,因此不考慮顯式構造函數S::S(int),並調用S::S(double)。另一方面,emplace_back執行直接初始化,因此都考慮S::S(double)S::S(int)。後者是一個更好的匹配,但它是private,所以該程序是格式不正確。

+0

+1我沒有想到這一點。整齊。 –

+2

的確,聰明。當我第一次看到這個問題時,我想,「好吧,'emplace_back'可以調用顯式構造函數」。然後我愚蠢地想,「但是從'emplace_back'到'push_back'時,這是一個突破性的改變,而不是相反。你可以在'push_back'不是''struct s {S(double){} explicit S(float){}};''的情況下'emplace_back'調用變得模糊不清:當然,這是一個*糟糕的*一組構造函數給'S'。 –

+0

那麼這是否意味着只要能夠成功編譯代碼,就不用擔心轉移到emplace_back? – BrandonSun

1

如果您在向量中保存的對象的拷貝構造函數中沒有任何瘋狂的副作用,那麼不會。

emplace_back被引入來優化不必要的複製和移動。

+0

另請注意,其中的一些優化也適用於'push_back'。 –

+0

是的,但編譯器*必須確定*構造函數是微不足道的,這樣做不會改變整體結果 – Kissiel

+0

我正在考慮通過移動捕獲,push_back現在移動感知。不知道你在想我的意思。 –

2

是的,您可以更改行爲(不僅僅是避免複製構造函數調用),因爲emplace_back只能看到不完全轉發的參數。

#include <iostream> 
#include <vector> 
using namespace std; 

struct Arg { Arg(int) {} }; 

struct S 
{ 
    S(Arg) { cout << "S(int)" << endl; } 
    S(void*) { cout << "S(void*)" << endl; } 
}; 

auto main() 
    -> int 
{ 
    vector<S>().ADD(0); 
} 

實施例構建:

 
[H:\dev\test\0011] 
> g++ foo.cpp -D ADD=emplace_back && a 
S(int) 

[H:\dev\test\0011] 
> g++ foo.cpp -D ADD=push_back && a 
S(void*) 

[H:\dev\test\0011] 
> _ 

補遺:如在his answer指出由Brian畢,另一個差,可導致不同的行爲是一個push_back呼叫涉及的隱式轉換到T,無視explicit構造函數和轉換運算符,而emplace_back使用直接初始化,它做c也可以嘗試explicit構造函數和轉換操作符。

+0

你能否更詳細地解釋這是如何工作的?我試圖弄清楚自己,但我真的很感興趣。 – Brian

+2

@BrianBi:轉換序列中不能有多個用戶定義的轉換。所以'null指針常量' - >'void *' - >'S'是唯一可用的'push_back'轉換。使用'emplace_back','0'是一個空指針常量的事實丟失了,因爲模板參數包無法捕獲它,它只是捕獲它是'int'(因此「不完全轉發」)。此外,在一個序列中不需要兩個用戶定義的轉換,因爲我們明確構造了一個「S」。所以'Arg'構造函數被選擇爲'S',並帶有隱式轉換'int' - >'Arg'。 –

+0

@SteveJessop:謝謝,因爲事實證明我沒有100%的理解我自己的示例代碼,直到我閱讀你對Brian的回覆。 :-) –

1

在異常情況下,emplace版本不會在所有處創建所需類型的對象。這可能會導致錯誤。

請看下面的例子,爲了簡單起見,使用std::vector(假設uptr行爲就像std::unique_ptr,除了構造函數不明確):

std::vector<uptr<T>> vec; 
vec.push_back(new T()); 

這是異常安全。創建一個臨時的uptr<T>傳遞到push_back,它被移動到矢量中。如果向量重新分配失敗,則分配的T仍由智能指針所擁有,該智能指針正確刪除它。

對比:

std::vector<uptr<T>> vec; 
vec.emplace_back(new T()); 

emplace_back不允許創建一個臨時對象。 ptr將在矢量中就地創建一次。如果重新分配失敗,就地創建沒有位置,並且不會創建智能指針。 T將被泄露。

當然,最好的辦法是:

std::vector<std::unique_ptr<T>> vec; 
vec.push_back(make_unique<T>()); 

這相當於第一,但讓智能指針創建明確的。

相關問題