web-dev-qa-db-fra.com

Existe-t-il un moyen standard de déplacer une plage dans un vecteur?

Considérez le programme suivant qui insère une gamme d'éléments dans un vecteur:

vector<string> v1;
vector<string> v2;

v1.Push_back("one");
v1.Push_back("two");
v1.Push_back("three");

v2.Push_back("four");
v2.Push_back("five");
v2.Push_back("six");

v1.insert(v1.end(), v2.begin(), v2.end());

Cela copie efficacement la plage, en allouant suffisamment d'espace dans le vecteur cible pour toute la plage afin qu'un maximum d'un redimensionnement soit nécessaire. Considérons maintenant le programme suivant qui tente de déplacer une plage dans un vecteur:

vector<string> v1;
vector<string> v2;

v1.Push_back("one");
v1.Push_back("two");
v1.Push_back("three");

v2.Push_back("four");
v2.Push_back("five");
v2.Push_back("six");

for_each ( v2.begin(), v2.end(), [&v1]( string & s )
{
    v1.emplace_back(std::move(s));
});

Ceci effectue un mouvement réussi mais ne bénéficie pas des avantages de insert () en ce qui concerne la préallocation d'espace dans le vecteur cible, de sorte que le vecteur peut être redimensionné plusieurs fois pendant l'opération.

Ma question est donc la suivante: existe-t-il un insert équivalent qui peut déplacer une plage dans un vecteur?

55
Benj

Vous utilisez un move_iterator avec insert:

v1.insert(v1.end(), make_move_iterator(v2.begin()), make_move_iterator(v2.end()));

L'exemple en 24.5.3 est presque exactement celui-ci.

Vous obtiendrez l'optimisation souhaitée si (a) vector::insert utilise la répartition des balises d'itérateur pour détecter l'itérateur à accès aléatoire et précalculer la taille (ce que vous avez supposé faire dans votre exemple qui copie), et (b) move_iterator conserve la catégorie d'itérateur de l'itérateur qu'il encapsule (ce qui est requis par la norme).

Sur un point obscur: je suis presque sûr que vector::insert peut mettre en place à partir de la source (ce qui n'est pas pertinent ici, puisque la source est du même type que la destination, donc une place est identique à une copie/déplacement, mais serait pertinente pour des exemples par ailleurs identiques). Je n'ai pas encore trouvé de déclaration indiquant qu'il est nécessaire de le faire, je viens de le déduire du fait que l'exigence sur la paire d'itérateurs i,j passé à insert est que T soit EmplaceConstructible de *i.

81
Steve Jessop
  1. std::move algorithme avec préallocation:

    #include <iterator>
    #include <algorithm>
    
    v1.reserve(v1.size() + v2.size()); // optional
    std::move(v2.begin(), v2.end(), std::back_inserter(v1));
    
  2. Les éléments suivants seraient encore plus flexibles:

    v1.insert(v1.end(), 
         std::make_move_iterator(v2.begin()), 
         std::make_move_iterator(v2.end()));
    

    Steve Jessop a fourni des informations générales sur ce qu'il fait et probablement comment il le fait.

33
sehe