Question push_back vs emplace_back


Je suis un peu confus concernant 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 un push_back surcharge en prenant une référence rvalue je ne vois pas tout à fait quel est le but de emplace_back devient?


521
2017-11-29 12:04


origine


Réponses:


En plus de ce que le visiteur a dit:

La fonction void emplace_back(Type&& _Val) fourni par MSCV10 est non conforme et redondant, car comme vous l'avez noté, il est strictement équivalent à 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 variée d'arguments, ce qui signifie que vous pouvez maintenant parfaitement transmettre les arguments et construire directement un objet dans un conteneur sans temporaire du tout.

C'est utile car peu importe l'intelligence de RVO et la sémantique du mouvement, il y a toujours des cas compliqués où un push_back est susceptible de faire des copies inutiles (ou de se déplacer). Par exemple, avec le traditionnel insert() 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 de emplace_back dans MSVC? En fait, il m'a aussi embêté il y a un certain temps, alors j'ai posé la même question sur le Blog Visual C ++. Voici la réponse de Stephan T Lavavej, le mainteneur officiel de l'implémentation de la bibliothèque standard Visual C ++ chez Microsoft.

Q: Les fonctions bêta 2 emplace sont-elles juste un type de placeholder en ce moment?

A: Comme vous le savez peut-être, les modèles variadiques   ne sont pas implémentés dans VC10. nous   les simuler avec le préprocesseur   machines pour des choses comme    make_shared<T>(), tuple et le nouveau   les choses dans <functional>. Ce   les machines de préprocesseur sont relativement   difficile à utiliser et à entretenir. Aussi,   cela affecte significativement la compilation   la vitesse, comme nous devons à plusieurs reprises   inclure des sous-titres. À cause de   combinaison de nos contraintes de temps   et les préoccupations de vitesse de compilation, nous   n'ont pas simulé de modèles variés   dans nos fonctions d'emplace.

Lorsque les modèles variés sont   implémenté dans le compilateur, vous pouvez   attendons que nous allons profiter de   eux dans les bibliothèques, y compris dans   nos fonctions emplace. Nous prenons   la conformité très au sérieux, mais   malheureusement, nous ne pouvons pas tout faire   tout à la fois.

C'est une décision compréhensible. Tous ceux qui ont essayé une seule fois d'émuler un modèle varié avec des trucs horribles du préprocesseur savent à quel point ce genre de choses est dégoûtant.


423
2017-11-29 18:00



emplace_back ne devrait pas prendre un argument de type vector::value_type, mais à la place des arguments variés 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.

Parce qu'il transmet les arguments, cela signifie que si vous n'avez pas rvalue, cela signifie toujours que le conteneur va stocker 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 devrait être identique à ce que push_back Est-ce que. 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

154
2017-11-29 12:47



Optimisation pour emplace_backpeut être démontré dans l'exemple suivant.

Pour emplace_back constructeur A (int x_arg) sera appelé. Et pour push_back  A (int x_arg) est appelé d'abord et move A (A &&rhs) est appelé après.

Bien sûr, le constructeur doit être marqué explicit, mais pour l'exemple actuel, 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 &&)

39
2017-10-27 16:42



emplace_back implémentation conforme transmettra des arguments à la vector<Object>::value_typeconstructeur lorsqu'il est ajouté au vecteur. Je me souviens que Visual Studio ne supportait pas les modèles variadiques, mais avec des modèles variés seront supportés dans Visual Studio 2013 RC, donc je suppose qu'une signature conforme sera ajoutée.

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

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


7
2017-09-24 04:39



Un de plus en cas de 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 {"element"});


1
2017-12-28 10:08



Un bon code pour les push_back et emplace_back est montré ici.

http://en.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.


1
2018-02-27 14:26