2010-11-29 68 views
469

我對push_backemplace_back之間的區別有點困惑。push_back vs emplace_back

void emplace_back(Type&& _Val); 
void push_back(const Type& _Val); 
void push_back(Type&& _Val); 

是有push_back超載服用右值引用我不太明白的emplace_back目的爲?

+11

一些很好的閱讀在這裏:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2642.pdf – 2010-11-29 14:00:37

+14

請注意,(如托馬斯下文所述),問題中的代碼來自MSVS的* C++ 0x模擬*,而不是C++ 0x的實際內容。 – me22 2010-12-21 05:50:14

+4

需要閱讀的更好的論文是:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2345.pdf。 N2642主要是標準的措詞; N2345是解釋和激勵這個想法的論文。 – Alan 2013-03-13 23:10:48

回答

384

除了什麼遊客說:

由MSCV10提供的功能void emplace_back(Type&& _Val)是不符合標準和多餘的,因爲當你注意到它是完全等同於push_back(Type&& _Val)

但是真正的C++ 0x表格emplace_back真的很有用:void emplace_back(Args&&...);

而不是採取value_type它需要一個可變參數列表,這意味着您現在可以完美地轉發參數並直接構造一個對象到容器中,而不需要任何暫時的。

這很有用,因爲無論有多聰明RVO和移動語義帶來的表格仍然存在複雜情況,push_back可能會產生不必要的副本(或移動)。例如,具有std::map傳統insert()功能,你必須創建一個臨時的,那麼這將被複制到一個std::pair<Key, Value>,然後將被複制到地圖:

std::map<int, Complicated> m; 
int anInt = 4; 
double aDouble = 5.0; 
std::string aString = "C++"; 

// cross your finger so that the optimizer is really good 
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString))); 

// should be easier for the optimizer 
m.emplace(4, anInt, aDouble, aString); 

那麼,爲什麼他們不在MSVC中實現emplace_back的正確版本?事實上,前段時間我也在竊聽我,所以我在Visual C++ blog上問了同樣的問題。這裏是Microsoft的Visual C++標準庫實現的官方維護者Stephan T Lavavej的回答。

問:現在beta2 emplace函數只是某種佔位符嗎?你可能知道,可變參數模板 沒有在VC10中實現。我們 模擬它們與預處理器 物聯網 make_shared<T>(),元組和 東西<functional>。這 預處理機器相對 難以使用和維護。另外, 它會顯着影響編譯 的速度,因爲我們不得不重複包含子標題 。由於我們的時間限制 和編譯速度問題的 的組合,我們 尚未在我們的emplace函數中模擬可變參數模板 。

當可變參數模板是 在編譯器實現的,你可以 預計我們將在圖書館採取的 他們的優勢,包括在 我們佈設功能。我們非常重視 的一致性,但不幸的是,我們不能一次完成所有的事情 。

這是一個可以理解的決定。每個試圖用預處理器可怕的技巧來模擬可變模板的人都知道這些東西有多噁心。

146

emplace_back不應接受類型爲vector::value_type的參數,而應將可變參數轉發給附加項目的構造函數。

template <class... Args> void emplace_back(Args&&... args); 

,能夠通過一個value_type其將被轉發到複製構造。

因爲它轉發參數,這意味着如果您沒有右值,這仍然意味着容器將存儲「複製」副本,而不是移動副本。

std::vector<std::string> vec; 
vec.emplace_back(std::string("Hello")); // moves 
std::string s; 
vec.emplace_back(s); //copies 

但是,上述內容應與push_back相同。這可能是相當意味着使用情況,如:

std::vector<std::pair<std::string, std::string> > vec; 
vec.emplace_back(std::string("Hello"), std::string("world")); 
// should end up invoking this constructor: 
//template<class U, class V> pair(U&& x, V&& y); 
//without making any copies of the strings 
7

emplace_back符合實現將參數添加到向量時將參數轉發到vector<Object>::value_type構造函數。我記得Visual Studio不支持可變模板,但在Visual Studio 2013 RC中將支持可變模板,所以我猜想會添加一個符合簽名。

使用emplace_back,如果直接將參數轉發給vector<Object>::value_type構造函數,則嚴格來說,不需要類型可移動或可複製的emplace_back函數。在vector<NonCopyableNonMovableObject>的情況下,這沒有用,因爲vector<Object>::value_type需要可複製或可移動的類型才能增長。

,這可能爲std::map<Key, NonCopyableNonMovableObject>是有用的,因爲一旦你分配在地圖中的條目,它並不需要移動或複製過了,不像vector,這意味着你可以有效地使用std::map使用既不可複製也不可移動的映射類型。

25

emplace_back的優化可以在下一個例子中演示。

對於emplace_back構造函數A (int x_arg)將被調用。並且對於 push_backA (int x_arg)先被調用,然後move A (A &&rhs)被調用。

當然,構造函數必須標記爲explicit,但是對於當前示例來說,很好地消除了顯式性。

#include <iostream> 
#include <vector> 
class A 
{ 
public: 
    A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; } 
    A() { x = 0; std::cout << "A()\n"; } 
    A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; } 
    A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; } 

private: 
    int x; 
}; 

int main() 
{ 
    { 
    std::vector<A> a; 
    std::cout << "call emplace_back:\n"; 
    a.emplace_back (0); 
    } 
    { 
    std::vector<A> a; 
    std::cout << "call push_back:\n"; 
    a.push_back (1); 
    } 
    return 0; 
} 

輸出:

call emplace_back: 
A (x_arg) 

call push_back: 
A (x_arg) 
A (A &&) 
2

還有一個在列表中的情況下:

//構造代替的元件。
emplace_back(「element」);

//它將創建新的對象,然後複製(或移動)其參數值。 push_back(explicitDataType {「element」});