4
votes

Existe-t-il une variante back_inserter qui tire parti de move?

Dans le code générique, j'essayais de dire à un itérateur de sortie (en pratique un std :: back_inserter_iterator pour déplacer une plage d'éléments. À ma grande surprise, il semblait que les éléments étaient déplacés dans un mouvement- opération to-back_inserter.

#include<algorithm> // move
#include<iterator> // back_inserter
#include<vector>
int main(){
    std::vector<std::vector<double> > v(10, std::vector<double>(100));
    std::vector<std::vector<double> > w;

    assert( not v[0].empty() and w.size() == 0 );
    std::copy(v.begin(), v.end(), std::back_inserter(w));
    assert( not v[0].empty() and w.size() == 10 );

    std::move(v.begin(), v.end(), std::back_inserter(w));
    assert( v[0].empty() and w.size() == 20 ); // were v elements moved to w?
}

Cependant, je ne pense pas qu'il soit possible que des éléments de v aient été réellement déplacés vers w , car après tout back_inserter fera un push_back qui implique une copie dans w .

Il semble plus probable que dans le std :: move case, les éléments v ont été déplacés vers un fichier temporaire et seulement ensuite copiés dans w .

Est-ce correct? Y a-t-il un std :: back_inserter qui bouge vraiment?

Existe-t-il déjà une variante de std :: back_inserter qui tire parti de move / emplace? Quelque chose comme un std::back_emplacer?


0 commentaires

3 Réponses :


2
votes

std :: back_inserter () est un modèle de fonction pratique pour créer un std :: back_insert_iterator objet.

La classe std :: back_insert_iterator a déjà operator = surchargé pour prendre les types rvalue reference , c'est-à-dire:

back_insert_iterator<Container>& operator=(typename Container::value_type&& value);

Par conséquent, std :: back_insert_iterator est déjà prêt à tirer parti de la sémantique de déplacement.


1 commentaires

J'ai manqué à la fois que back_inserter_iterator a operator = (T &&) et que push_back a également un T && .



3
votes

Si std :: back_insert_iterator ne fait rien d'autre qu'appeler push_back sur le conteneur avec lequel il est en cours de construction, la question peut être répondue en regardant le std :: vector :: push_back jeu de surcharge:

back_insert_iterator<Container>&
    operator=(typename Container::const_reference value);
back_insert_iterator<Container>&
    operator=(const typename Container::value_type& value);

Donc, ici, nous avons évidemment une surcharge pour les références rvalue. Ne serait-il pas étrange que le std :: back_insert_iterator correspondant copie son argument lorsqu'il est appelé avec une référence rvalue? Et en effet, nous avons à nouveau un ensemble de surcharge très similaire:

void push_back(const T& value);
void push_back(T&& value);

avec le commentaire supplémentaire que

1) Résultats dans container-> push_back (valeur)
2) Résultats dans container-> push_back (std :: move (value))

Pour revenir à votre question initiale

Y a-t-il un std :: back_inserter qui bouge vraiment?

Oui, l'itérateur créé par cette fonction tire parti des arguments de référence rvalue.

Existe-t-il déjà une variante de std :: back_inserter qui tire parti de move / emplace?

Non, mais cela peut être implémenté. Consultez cette réponse .


5 commentaires

J'ai raté ça, j'étais convaincu que push_back n'avait qu'une surcharge de const T & . Cela ajouté au fait que back_inserter_iterator a :: operator = (T &&) rend un back_emplacer beaucoup moins nécessaire.


@alfC Je suppose que vous vous confondez avec emplace vs move. Le déplacement du contenu - évitant ainsi une copie et une suppression - est la fonctionnalité beaucoup plus simple à implémenter, et vous pouvez à peu près supposer qu'elle est partout dans le stl. Avec emplace, vous fournissez les arguments nécessaires pour construire l'objet et appelez une méthode spéciale xxx_emplace () pour que l'objet soit construit directement dans l'espace conteneur. Cela peut, bien sûr, être un constructeur de mouvement, mais il n'est généralement pas nécessaire de le mettre en place si tout ce que vous voulez faire est de déplacer.


@GemTaylor oui, mais si vous parlez d'emplace avec un seul argument de type valeur. Alors push_back (move (t)) n'est ni pire ni meilleur que emplace_back (move (t)). C'est ça?


@alfC Non, cela ne devrait pas être très différent, en supposant que votre objet implémente la sémantique efficacement, et que l'objet principal lui-même n'est pas si grand que copier ces simples membres ne coûte pas cher. Par exemple, déplacer une chaîne signifie copier au moins 24 octets de l'objet chaîne lui-même. Emplace évite ces 24 octets: rien comparé à l'allocation d'une nouvelle chaîne, la copie du contenu et la suppression de l'ancienne chaîne, mais pas de zéro.


Pour être clair, emplace_back d'un char * dans un vecteur de chaîne enregistrera cette petite copie supplémentaire et effacera l'objet chaîne de 24 octets lui-même par rapport à push_back du même char * - bien sûr, la chaîne doit être copiée une fois dans la mémoire dynamique dans les deux cas, ce qui est une opération beaucoup plus coûteuse. Mais comparez cela à C ++ 98, lorsque push_back de char * créerait une chaîne temporaire, puis dupliquerait le contenu dynamique, puis supprimerait la première instance dynamique.



1
votes

Existe-t-il déjà une variante de std :: back_inserter qui en profite de déménagement / mise en place? Quelque chose comme un std :: back_emplacer?

Il y a back_emplacer décrit ici: https://stackoverflow.com/a/12131700/7471760

Mais il ne fait pas partie de std.

Voir aussi: Pourquoi pas d'itérateurs de mise en place en C ++ 11 ou C ++ 14? < / a>


0 commentaires