push_back vs emplace_back


761

Biraz arasındaki fark ile ilgili karıştı değilim push_backve emplace_back.

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

push_backBir rvalue referansı alan bir aşırı yük olduğundan , amacının ne olduğunu tam olarak göremiyorum emplace_back?



16
(Thomas'ın aşağıda söylediği gibi), söz konusu kodun MSVS'nin C ++ 0x öykünmesinden kaynaklandığını, C ++ 0x'in gerçekte ne olduğunu unutmayın.
me22

5
Okumak için daha iyi bir makale: open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2345.pdf . N2642 çoğunlukla Standart için yazılmıştır; N2345, fikri açıklayan ve motive eden yazıdır.
Alan

MSVC10'da bile, tek argüman kurucularına mükemmel yönlendirme sağlayan evrensel bir referanstemplate <class _Valty> void emplace_back(_Valty&& _Val) alan bir sürüm olduğunu unutmayın . explicit
joki

İlgili: push_backTercih edilebilecek herhangi bir durum var mı emplace_back? Düşünebileceğim tek durum, bir sınıfın bir şekilde copyable ( T&operator=(constT&)) olduğu, ancak yapılı ( T(constT&)) olmadığı, ancak birinin neden bunu isteyeceğini düşünemiyorum.
Ben

Yanıtlar:


568

Ziyaretçilerin söylediklerine ek olarak:

void emplace_back(Type&& _Val)MSCV10 tarafından sağlanan işlev uyumsuz ve gereksizdir, çünkü belirttiğiniz gibi kesinlikle buna eşdeğerdir push_back(Type&& _Val).

Ancak gerçek C ++ 0x formu emplace_backgerçekten yararlıdır void emplace_back(Args&&...):;

value_typeBunu almak yerine değişken bir argüman listesi alır, böylece artık argümanları mükemmel bir şekilde iletebilir ve geçici olarak hiç bir nesneyi doğrudan bir kaba inşa edebilirsiniz.

Ne kadar zekice RVO ve semantik hareket tabloya getirmek olursa olsun, bir push_back'in gereksiz kopyalar oluşturması (veya taşıması) hala karmaşık durumlar vardır. Örneğin, a'nın geleneksel insert()işleviyle, std::mapdaha sonra a'ya kopyalanacak ve std::pair<Key, Value>daha sonra haritaya kopyalanacak bir geçici oluşturmanız gerekir:

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);

Peki neden MSVC'de emplace_back'in doğru sürümünü uygulamadılar? Aslında, bir süre önce beni rahatsız etti, bu yüzden aynı soruyu Visual C ++ blogunda sordum . İşte Microsoft'un Visual C ++ standart kütüphane uygulamasının resmi sürdürücüsü Stephan T Lavavej'in cevabı.

S: Beta 2 emplace fonksiyonları şu anda sadece bir çeşit yer tutucu mu?

C: Bildiğiniz gibi VC10'da varyasyon şablonları uygulanmaz. Onları make_shared<T>(), tuple ve içindeki yeni şeyler için önişlemci makineleri ile simüle ediyoruz <functional>. Bu önişlemci makinesinin kullanımı ve bakımı nispeten zordur. Ayrıca, alt başlıkları tekrar tekrar eklememiz gerektiğinden derleme hızını önemli ölçüde etkiler. Zaman kısıtlamaları ve derleme hızı endişelerimizin bir kombinasyonu nedeniyle, emplace fonksiyonlarımızda varyasyon şablonları simüle etmedik.

Derleyiciye varyasyon şablonları uygulandığında, emplace fonksiyonlarımız da dahil olmak üzere kütüphanelerden bunlardan faydalanmamızı bekleyebilirsiniz. Uyumu çok ciddiye alıyoruz, ancak ne yazık ki, her şeyi aynı anda yapamayız.

Bu anlaşılabilir bir karardır. Önişlemci korkunç hileleri ile varyasyon şablonu taklit etmeye sadece bir kez deneyen herkes, bu şeylerin ne kadar iğrenç olduğunu bilir.


101
Bunun bir C ++ sorunu değil, bir MSVS10 sorunu olduğuna dair bu açıklama burada en önemli kısımdır. Teşekkürler.
me22

11
C ++ kodunun son satırının çalışmayacağını düşünüyorum. pair<const int,Complicated>int, another int, double ve 4th parametre olarak dize alan bir kurucuya sahip değildir. Ancak, olabilir doğrudan parçalı-yapıcısı kullanarak bu çifti nesne oluşturmak. Sözdizimi farklı olacaktır, elbette:m.emplace(std::piecewise,std::forward_as_tuple(4),std::forward_as_tuple(anInt,aDouble,aString));
sellibitze

3
Mutlulukla varyasyon şablonları VS2013, şimdi önizleme olacak.
Daniel Earwicker

11
bu cevap vs2013'teki yeni gelişmeleri yansıtacak şekilde güncellenmeli mi?
becko

6
Daha sonra Visual Studio 2013 kullanılarak veya iseniz şimdi , sen "gerçek" için destek olmalıdır emplace_back: Bu variadic şablonları eklendi Visual C ++ içine uygulanan sürece msdn.microsoft.com/en-us/library/hh567368. aspx
kayleeFrye_onDeck

200

emplace_backtür argümanı almamalı vector::value_type, bunun yerine ekteki öğenin yapıcısına iletilen varyasyonlu bağımsız değişkenler almalıdır .

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

value_typeKopya yapıcıya iletilecek olanı geçmek mümkündür .

Argümanları ilettiği için, bu, eğer rvalue'nuz yoksa, konteynerin taşınan bir kopyayı değil, "kopyalanmış" bir kopya saklayacağı anlamına gelir.

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

Ancak yukarıdakiler ne ile aynı olmalıdır push_back. Muhtemelen aşağıdaki gibi kullanım durumları içindir:

 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

2
@David: ama o zaman skapsamınız değişti , bu tehlikeli değil mi?
Matthieu M.

2
Artık değerini s olarak kullanmayı planlamıyorsanız tehlikeli değildir. Taşıma işlemi s'yi geçersiz kılmaz, taşıma işlemi yalnızca önceden s olarak yapılan dahili bellek ayırmayı çalacak ve imha edildiğinde std :: string str yazmış gibi iyi olacak olan varsayılan bir durumda bırakacaktır (ayrılmış bir ayırma yok);
David

4
@David: Bir taşınan nesnenin sonraki yıkım dışında herhangi bir kullanım için geçerli olduğundan emin değilim.
Ben Voigt

46
vec.emplace_back("Hello")çünkü çalışacak const char*argüman olacaktır iletilen için stringyapıcı. Bütün mesele bu emplace_back.
Alexandre

8
@BenVoigt: Taşınan bir nesnenin geçerli (ancak belirtilmemiş) durumda olması gerekir. Ancak bu, üzerinde herhangi bir işlem yapabileceğiniz anlamına gelmez. Düşünün std::vector. Boş std::vector, geçerli bir durumdur, ancak bunu çağıramazsınız front(). Bu, önkoşulu olmayan herhangi bir fonksiyonun hala çağrılabileceği anlamına gelir (ve yıkıcıların hiçbir zaman önkoşulu olamaz).
David Stone

96

İçin optimizasyon bir emplace_backsonraki örnekte gösterilebilir.

İçin emplace_backyapıcı A (int x_arg)çağrılır. Ve push_back A (int x_arg)ilk move A (A &&rhs)olarak çağrılır ve daha sonra çağrılır.

Tabii ki, kurucu olarak işaretlenmelidir explicit, ancak mevcut örnek için açıklığı kaldırmak iyidir.

#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;
}

çıktı:

call emplace_back:
A (x_arg)

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

21
emplace_backVs çağrılırken gerçekte ne olduğunu gösteren kod örneği için +1 push_back.
Shawn

Ben v.emplace_back(x);burada x açıkça taşı-inşa ama sadece açıkça kopya-yapılı olduğunu çağıran kod olduğunu fark ettikten sonra geldi . emplace_back"Örtük olarak" açık olan gerçek , beni ekleme için gitme işlevimin muhtemelen olması gerektiğini düşündürüyor push_back. Düşünceler?
Ben

a.emplace_backİkinci kez ararsanız , hamle yapıcısı çağrılır!
X Æ A-12


8

emplace_backUYGULAMA UYGULAMASI vector<Object>::value_type, vektöre eklendiğinde argümanları yapıcıya iletir . Visual Studio'nun varyasyonlu şablonları desteklemediğini hatırlıyorum, ancak varyasyonlu şablonlar ile Visual Studio 2013 RC'de desteklenecek, bu yüzden uygun bir imza eklenecek.

İle emplace_backdoğrudan argümanlar ileri eğer vector<Object>::value_typeyapıcı, bir tür gerekmez için hareketli veya copyable olmak emplace_backaçıkçası, işlevi. Bu vector<NonCopyableNonMovableObject>durumda, vector<Object>::value_type büyümek için kopyalanabilir veya taşınabilir bir türe ihtiyaç duyduğundan , bu yararlı değildir .

Ama not Bunun için yararlı olabileceğini std::map<Key, NonCopyableNonMovableObject>haritanın bir girdiyi tahsis kez taşınması veya farklı olarak, artık hiç kopyalanması gerekmediğinden, vectorkullanabileceğiniz anlamınastd::map ne de haritalanmış bir türle etkili bir hareket edebilir.


8

Listelerde bir tane daha:

// constructs the elements in place.                                                
emplace_back("element");


//It will create new object and then copy(or move) its value of arguments.
push_back(explicitDataType{"element"});

1

Özel kullanım örneği emplace_back: Daha sonra bir kaba aktarılacak geçici bir nesne oluşturmanız gerekirse emplace_back,push_back . Nesneyi konteyner içinde yerinde oluşturacaktır.

Notlar:

  1. push_backYukarıdaki durumda geçici bir nesne oluşturacak ve onu kaba taşıyacaktır. Bununla birlikte, kullanılan yerinde yapı emplace_back, nesneyi inşa etmekten ve sonra hareket etmekten daha performanslıdır (genellikle bir miktar kopyalamayı içerir).
  2. Genel olarak, tüm durumlar emplace_backyerine push_backçok fazla sorun olmadan kullanabilirsiniz . ( İstisnalara bakın )
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.