[英]push_back vs emplace_back
我對push_back
和emplace_back
之間的區別有點困惑。
void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);
由於存在采用右值引用的push_back
重載,我不太明白emplace_back
的目的是什么?
除了訪客所說的:
void emplace_back(Type&& _Val)
提供的函數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>
,然后將其復制到 map 中:
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++ 博客上問了同樣的問題。 這是微軟 Visual C++ 標准庫實現的官方維護者 Stephan T Lavavej 的回答。
問:beta 2 emplace 函數現在只是某種占位符嗎?
答:您可能知道,VC10 中沒有實現可變參數模板。 我們使用預處理器機制模擬它們,例如
make_shared<T>()
、元組和<functional>
的新事物。 這種預處理機器相對難以使用和維護。 此外,它會顯着影響編譯速度,因為我們必須反復包含子標題。 由於我們的時間限制和編譯速度問題,我們沒有在 emplace 函數中模擬可變參數模板。當在編譯器中實現可變參數模板時,您可以預期我們將在庫中利用它們,包括在我們的 emplace 函數中。 我們非常重視一致性,但不幸的是,我們無法一次完成所有事情。
這是一個可以理解的決定。 每個嘗試過使用預處理器可怕技巧模擬可變參數模板的人都知道這東西有多惡心。
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
emplace_back
優化可以在下一個示例中演示。
對於emplace_back
構造函數A (int x_arg)
將被調用。 對於push_back
首先調用A (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 &&)
列表的另一個示例:
// constructs the elements in place.
emplace_back("element");
// creates a new object and then copies (or moves) that object.
push_back(ExplicitDataType{"element"});
此處顯示了 push_back 和 emplace_back 的一個很好的代碼。
http://en.cppreference.com/w/cpp/container/vector/emplace_back
您可以在 push_back 而非 emplace_back 上看到移動操作。
當添加到向量時,符合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
有效地使用既不可復制也不可移動的映射類型。
emplace_back
特定用例:如果您需要創建一個臨時對象,然后將其推送到容器中,請使用emplace_back
而不是push_back
。 它將在容器內就地創建對象。
筆記:
push_back
將創建一個臨時對象並將其移動到容器中。 但是,用於emplace_back
的就地構造比構造然后移動對象(通常涉及一些復制)的性能更高。emplace_back
而不是push_back
沒有太大問題。 (見例外)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.