web-dev-qa-db-fra.com

push_back vs emplace_back

Je suis un peu confus en ce qui concerne 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 référence rvalue, je ne vois pas très bien à quoi sert l'objectif de emplace_back?

650
ronag

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 indiqué, 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 faut une liste d'arguments variadique, ce qui signifie que vous pouvez désormais parfaitement transférer les arguments et construire directement un objet dans un conteneur sans aucun temporaire.

C'est utile car, peu importe le degré d'intelligence que RVO et le mouvement apportent à la table, il existe encore des cas complexes où un Push_back est susceptible de faire des copies inutiles (ou de se déplacer). Par exemple, avec la fonction insert() traditionnelle 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 de emplace_back dans MSVC? En fait, cela m’a trop perturbé il ya quelque temps, alors j’ai posé la même question sur le blog Visual C++ . Voici la réponse de Stephan T Lavavej, responsable officiel de la mise en œuvre de la bibliothèque standard Visual C++ chez Microsoft.

Q: Est-ce que les fonctions bêta 2 en place ne sont qu’un type d’espace réservé actuellement?

R: Comme vous le savez peut-être, les modèles variadiques ne sont pas implémentés dans VC10. Nous les simulons avec des machines de préprocesseur pour des choses telles que make_shared<T>(), Tuple et les nouvelles choses dans <functional>. Cette machine de pré-traitement est relativement difficile à utiliser et à entretenir. En outre, cela affecte considérablement la vitesse de compilation, car nous devons inclure à plusieurs reprises des sous-en-têtes. En raison d'une combinaison de contraintes de temps et de problèmes de rapidité de compilation, nous n'avons pas simulé de modèles variadiques dans nos fonctions de création.

Lorsque des modèles variadiques sont implémentés dans le compilateur, vous pouvez vous attendre à en tirer parti dans les bibliothèques, y compris dans nos fonctions d’emplace. Nous prenons la conformité très au sérieux, mais malheureusement, nous ne pouvons pas tout faire en même temps.

C'est une décision compréhensible. Tous ceux qui n’ont essayé qu’une fois d’émuler un modèle variadique avec d’horribles tours du préprocesseur savent à quel point ces éléments sont dégoutants.

504
Thomas Petit

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 un value_type qui sera transmis au constructeur de la copie.

Comme cela transmet les arguments, cela signifie que si vous n'avez pas rvalue, cela signifie que le conteneur stockera une copie "copiée" et non 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 devrait être identique à ce que Push_back fait. Il est probablement plutôt destiné à des cas d'utilisation tels que:

 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
182
visitor

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

Pour emplace_back, le constructeur A (int x_arg) sera appelé. Et pour Push_backA (int x_arg) est appelé en premier et move A (A &&rhs) est appelé ensuite.

Bien sûr, le constructeur doit être marqué comme explicit, mais pour l'exemple en cours, il est bon de supprimer le caractère explicite.

#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 &&)
61
vadikrobot

emplace_back l'implémentation conforme transmettra les arguments au vector<Object>::value_typeconstructor une fois ajoutés au vecteur. Je me souviens que Visual Studio n’a pas pris en charge les modèles variadiques, mais avec les modèles variadiques étant pris en charge dans Visual Studio 2013 RC, je suppose qu’une signature conforme sera ajoutée.

Avec emplace_back, si vous transférez les arguments directement au constructeur vector<Object>::value_type, vous n'avez pas besoin qu'un type soit déplaçable ou copiable pour la fonction emplace_back à proprement parler. Dans le cas vector<NonCopyableNonMovableObject>, cela n'est pas utile, car vector<Object>::value_type a besoin d'un type pouvant être copié ou déplacé pour se développer.

Mais note que cela pourrait être utile pour std::map<Key, NonCopyableNonMovableObject>, puisqu’une fois que vous affectez une entrée à la carte, elle n’a plus besoin d’être déplacée ni copiée, contrairement à vector, ce qui signifie que vous pouvez utiliser efficacement std::map avec un type mappé qui ne peut être ni copié ni copié.

8
Germán Diago

Un code de Nice pour Push_back et emplace_back est montré ici.

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

Vous pouvez voir l'opération de déplacement sur Push_back et non sur emplace_back.

6
Dharma

Un de plus en cas de listes:

// 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"});
5
user3847351