repousser vs emplace dos

je suis un peu confus quant à la différence entre push_back et emplace_back .

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

comme il y a une surcharge push_back prenant une valeur de référence Je ne vois pas tout à fait quel est le but de emplace_back devient?

543
demandé sur Ajay 2010-11-29 15:04:09

6 réponses

en plus de ce que le visiteur a dit:

la fonction void emplace_back(Type&& _Val) fournie par MSCV10 est non conforme et redondante, car comme vous l'avez noté, elle est strictement équivalente à push_back(Type&& _Val) .

mais la vraie forme C++0x de emplace_back est vraiment utile: void emplace_back(Args&&...) ;

au lieu de prendre un value_type il prend une liste variadique d'arguments, de sorte que cela signifie que vous pouvez maintenant parfaitement en avant les arguments et construire directement un objet dans un contenant sans que temporaire.

c'est utile parce que peu importe combien d'intelligence RVO et move sémantic apportent à la table il y a encore des cas compliqués où un push_back est susceptible de faire des copies (ou move) inutiles. Par exemple, avec la traditionnelle insert() la fonction d'un std::map , vous devez créer un temporaire, qui sera ensuite copié dans un std::pair<Key, Value> , qui sera ensuite copié dans la carte:

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

alors pourquoi n'ont-ils pas implémenté la bonne version d'emplace_back dans MSVC? En fait, il m'a mis sur écoute aussi il y a un moment, donc j'ai posé la même question sur le Visual C++ blog . Voici la réponse de Stephan t Lavavej, le responsable officiel de L'implémentation de la bibliothèque standard Visual C++ chez Microsoft.

Q: sont bêta 2 fonctions emplace juste une sorte de placeholder droit maintenant?

A: comme vous le savez peut-être, modèles variadiques ne sont pas implémentés dans VC10. Nous simuler avec préprocesseur machines pour des choses comme make_shared<T>() , tuple, et le nouveau les choses dans <functional> . Ce préprocesseur machines est relativement difficile à utiliser et à entretenir. Également, il affecte de manière significative la compilation vitesse, comme nous devons à plusieurs reprises inclure subheaders. En raison d'un la combinaison de nos contraintes de temps et vitesse de compilation préoccupations, nous Je n'ai pas simulé de modèles variadiques dans nos fonctions emplace.

quand les gabarits variadiques sont mis en œuvre dans le compilateur, vous pouvez attendez-vous à ce que nous en profitions dans les bibliothèques, y compris dans nos fonctions emplace. Nous prenons conformité très sérieusement, mais malheureusement, nous ne pouvons pas tout faire tout à la fois.

C'est une décision compréhensible. Tous ceux qui ont essayé juste une fois d'émuler modèle variadique avec préprocesseur trucs horribles sait à quel point ce genre de choses devient dégoûtant.

440
répondu Thomas Petit 2018-06-26 21:30:17

emplace_back ne devrait pas prendre un argument de type vector::value_type , mais plutôt des arguments variadiques qui sont transmis au constructeur de l'élément ajouté.

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

Il est possible de passer d'un value_type , qui sera transmis au constructeur de copie.

parce qu'il transmet les arguments, cela signifie que si vous n'avez pas de valeur, cela signifie encore que le conteneur stockera une copie" copiée", pas une copie déplacée.

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

mais ce qui précède doit être identique à ce que push_back fait. Il est probablement plutôt destiné à des cas d'utilisation comme:

 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
160
répondu visitor 2010-11-29 12:52:57
L'optimisation

pour emplace_back peut être démontrée dans l'exemple suivant.

pour emplace_back constructeur A (int x_arg) sera appelé. Et pour push_back A (int x_arg) s'appelle d'abord et move A (A &&rhs) s'appelle ensuite.

bien sûr, le constructeur doit être marqué comme explicit , mais pour l'exemple actuel est bon pour supprimer explicitness.

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

sortie:

call emplace_back:
A (x_arg)

call push_back:
A (x_arg)
A (A &&)
43
répondu vadikrobot 2018-09-14 09:15:18

emplace_back conforme mise en œuvre va de l'avant des arguments à l' vector<Object>::value_type constructeur lorsqu'il est ajouté au vecteur. Je me souviens que Visual Studio n'a pas pris en charge les gabarits variadic, mais avec variadic les gabarits seront pris en charge dans Visual Studio 2013 RC, donc je suppose qu'une signature conforme sera ajoutée.

avec emplace_back , si vous transmettez les arguments directement au constructeur vector<Object>::value_type , vous n'avez pas besoin d'un type pour être mobile ou copiable pour emplace_back fonction, à proprement parler. Dans le cas vector<NonCopyableNonMovableObject> , cela n'est pas utile, car vector<Object>::value_type a besoin d'un type copiable ou mobile pour se développer.

mais notez que cela pourrait être utile pour std::map<Key, NonCopyableNonMovableObject> , car une fois que vous attribuez une entrée dans la carte, elle n'a plus besoin d'être déplacée ou copiée, contrairement à vector , ce qui signifie que vous pouvez utiliser std::map efficacement avec un type cartographié qui n'est ni copiable ni Mobile.

7
répondu Germán Diago 2016-08-17 08:58:08

un de plus pour les listes:

// construit les éléments en place.

emplace_back("élément");

/ / il va créer un nouvel objet et ensuite copier(ou déplacer) sa valeur d'arguments. push_back(explicitDataType{"élément"});

1
répondu 2017-12-28 10:15:28

un joli code pour push_back et emplace_back est affiché ici.

http://en.cppreference.com/w/cpp/container/vector/emplace_back

, Vous pouvez voir l'opération de déplacement sur push_back et pas sur emplace_back.

1
répondu Dharma 2018-02-27 14:26:20