Pourquoi j'utiliserais push back au lieu de emplace back?
  C++11 vecteurs ont la nouvelle fonction  emplace_back  . Contrairement à  push_back , qui s'appuie sur les optimisations des compilateurs pour éviter les copies, emplace_back  utilise la transmission parfaite pour envoyer les arguments directement au constructeur pour créer un objet en place. Il me semble que  emplace_back  fait tout ce que  push_back  peut faire, mais parfois il le fera mieux (mais jamais pire).  
  Quelle raison dois-je utiliser  push_back  ?  
4 réponses
   push_back  permet toujours l'utilisation de l'initialisation uniforme, ce que j'aime beaucoup. Par exemple:  
struct aggregate {
    int foo;
    int bar;
};
std::vector<aggregate> v;
v.push_back({ 42, 121 });
  
   par contre, v.emplace_back({ 42, 121 });  ne fonctionnera pas.  
  j'ai beaucoup réfléchi à cette question au cours des quatre dernières années. Je suis arrivé à la conclusion que la plupart des explications sur  push_back  vs.  emplace_back  manquent le tableau complet.  
 L'année dernière, j'ai donné une présentation à C++maintenant sur  déduction de Type dans C++14  . Je commence à parler de  push_back  vs.  emplace_back  à 13:49, mais il y a des informations utiles qui fournissent des preuves à l'appui avant cela.  
  la vraie différence primaire concerne les constructeurs implicites et les constructeurs explicites. Considérons le cas où nous avons un seul argument que nous voulons passer à  push_back  ou  emplace_back  .  
std::vector<T> v;
v.push_back(x);
v.emplace_back(x);
  
    après que votre compilateur d'optimisation ait mis la main dessus, il n'y a pas de différence entre ces deux énoncés en termes de code généré. La sagesse traditionnelle est que  push_back  construira un objet temporaire, qui sera ensuite déplacé dans   v attendu que emplace_back  acheminera l'argument et le construira directement en place sans copies ni mouvements. Cela peut être vrai en se basant sur le code tel qu'écrit dans les bibliothèques standards, mais cela fait l'hypothèse erronée que l'optimisation du travail du compilateur est de générer le code que vous avez écrit. Le travail du compilateur d'optimisation est en fait de générer le code que vous auriez écrit Si vous étiez un expert des optimisations spécifiques à la plate-forme et ne vous souciez pas de la maintenabilité, juste performance.  
 la différence réelle entre ces deux affirmations est que le plus puissant emplace_back appellera n'importe quel type de constructeur là-bas, tandis que le plus prudent push_back  appellera seulement les constructeurs qui sont implicites. Les constructeurs implicites sont censés être sûrs. Si vous pouvez implicitement construire un  U à partir d'un T , vous dites que U peut contenir toute l'information dans T  sans perte. Il est en sécurité dans à peu près n'importe quelle situation pour passer un  T  et personne ne s'en souciera si vous faites un  U  à la place. Un bon exemple de constructeur implicite est la conversion de  std::uint32_t  en  std::uint64_t  . Un mauvais exemple de conversion implicite est  double en std::uint8_t .  
  Nous voulons être prudents dans notre programmation. Nous ne voulons pas utiliser des fonctionnalités puissantes parce que plus la fonctionnalité est puissante, plus il est facile de faire accidentellement quelque chose de incorrect ou inattendu. Si vous avez l'intention d'appeler des constructeurs explicites, alors vous avez besoin de la puissance de  emplace_back  . Si vous voulez appeler seulement les constructeurs implicites, s'en tenir à la sécurité de  push_back  .  
un exemple
std::vector<std::unique_ptr<T>> v;
T a;
v.emplace_back(std::addressof(a)); // compiles
v.push_back(std::addressof(a)); // fails to compile
  
     std::unique_ptr<T>  a un constructeur explicite de  T * . Parce que  emplace_back  peut appeler des constructeurs explicites, en passant un pointeur non propriétaire compile très bien. Toutefois, lorsque  v  va hors de portée, le destructeur tentera d'appeler  delete  sur ce pointeur, qui n'a pas été attribué par  new  parce que c'est juste un objet de pile. Cela conduit à un comportement indéfini.  
  ce n'est pas qu'un code inventé. C'était un vrai bug de production que j'ai rencontré. Le code était  std::vector<T *> , mais il possédait le contenu. Dans le cadre de la migration vers C++11, j'ai correctement changé  T *  en  std::unique_ptr<T>  pour indiquer que le vecteur possédait sa mémoire. Cependant, J'ai basé ces changements sur ma compréhension en 2012, au cours de laquelle je me suis dit " emplace_back fait tout ce que push_back peut faire et plus encore, alors pourquoi est-ce que j'utiliserais push_back?", donc j'ai aussi changé le push_back en emplace_back .  
 si j'avais plutôt quitté le code en utilisant le plus sûr push_back , j'aurais instantanément attrapé ce bogue de longue date et il aurait été considéré comme un succès de la mise à niveau vers C++11. Au lieu de ça, j'ai masqué le bug et je ne l'ai pas trouvé avant des mois. tard.  
certaines implémentations de bibliothèque d'emplace_back ne se comportent pas comme spécifié dans la norme C++ incluant la version qui est envoyée avec Visual Studio 2012, 2013 et 2015.
 afin d'accommoder les bogues connus du compilateur, il est préférable d'utiliser std::vector::push_back()  si les paramètres renvoient à des itérateurs ou à d'autres objets qui seront invalides après l'appel.  
std::vector<int> v;
v.emplace_back(123);
v.emplace_back(v[0]); // Produces incorrect results in some compilers
  
    sur un compilateur, v contient les valeurs 123 et 21 au lieu des 123 et 123 attendus. Cela est dû au fait que le 2ème appel à  emplace_back  se traduit par un redimensionnement à partir duquel  v[0]  devient invalide.  
  une application pratique du code ci-dessus utiliserait  push_back()  au lieu de  emplace_back()  comme suit:  
std::vector<int> v;
v.emplace_back(123);
v.push_back(v[0]);
  
    Note: l'utilisation d'un vecteur d'ints est à des fins de démonstration. J'ai découvert ce problème avec une classe beaucoup plus complexe qui incluait des variables membres allouées dynamiquement et l'appel à   emplace_back()  a causé un accident.