Les réponses données ne concaténent pas réellement. Ils en joignent une copie. Il peut être utile (pour le point de vue de l'efficacité) de créer une méthode de concaténation std :: vector, mais cela nécessiterait un partage sophistiqué de la gestion des nœuds et c'est probablement pourquoi cela n'a pas été fait.
FauChristian
8
@FauChristian: Non, il peut ne pas y avoir d'utilisation du point de vue de l'efficacité. La mémoire vectorielle doit être continue, donc ce que l'on vous suggère est impossible. Si vous vouliez "un partage sophistiqué de la gestion des nœuds", et si vous deviez changer la classe vectorielle de cette manière, vous vous retrouveriez avec un deque. Même alors, il est très difficile de réutiliser la mémoire de la manière suggérée, même si cela commencerait à être un peu plus réalisable. Je ne pense pas qu'il soit actuellement mis en œuvre. L'essentiel est que dans un tel partage de nœuds de gestion (un deque) le nœud final puisse être partiellement vide.
Cookie
4
@lecaruyer Vous vous rendez compte que vous venez de marquer une question qui a été posée deux ans auparavant en double
eshirima
9
Suis-je le seul à me demander pourquoi cela n'est pas implémenté comme a + bou a.concat(b)dans la bibliothèque standard? Peut-être que l'implémentation par défaut serait sous-optimale, mais chaque concaténation de tableau n'a pas besoin d'être micro-optimisée
oseiskar
10
des années d'évolution, la surcharge d'opérateur la plus avancée de tous les langages traditionnels, un système de modèles qui double la complexité du langage, et pourtant la réponse n'est pas v = v1 + v2;
Je voudrais seulement ajouter du code pour obtenir d'abord le nombre d'éléments que chaque vecteur contient, et définir vector1 pour être celui qui contient le plus grand. Si vous faites autrement, vous faites beaucoup de copies inutiles.
Joe Pineda
34
J'ai une question. Est-ce que cela fonctionnera si vector1 et vector2 sont les mêmes vecteurs?
Alexander Rafferty
6
Si vous avez concaténé plusieurs vecteurs en un, est-il utile d'appeler reserved'abord le vecteur de destination?
Faheem Mitha
33
@AlexanderRafferty: Seulement si vector1.capacity() >= 2 * vector1.size(). Ce qui est atypique sauf si vous avez appelé std::vector::reserve(). Sinon, le vecteur se réallouera, invalidant les itérateurs passés en paramètres 2 et 3.
Drew Dormann
28
C'est dommage qu'il n'y ait pas d'expression plus succincte dans la bibliothèque standard. .concatou +=ou quelque chose
nmr
193
Si vous utilisez C ++ 11 et souhaitez déplacer les éléments plutôt que de simplement les copier, vous pouvez utiliser std::move_iteratoravec insert (ou copy):
#include<vector>#include<iostream>#include<iterator>int main(int argc,char** argv){
std::vector<int> dest{1,2,3,4,5};
std::vector<int> src{6,7,8,9,10};// Move elements from src to dest.// src is left in undefined but safe-to-destruct state.
dest.insert(
dest.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()));// Print out concatenated vector.
std::copy(
dest.begin(),
dest.end(),
std::ostream_iterator<int>(std::cout,"\n"));return0;}
Cela ne sera pas plus efficace pour l'exemple avec des entiers, car les déplacer n'est pas plus efficace que les copier, mais pour une structure de données avec des mouvements optimisés, cela peut éviter de copier un état inutile:
#include<vector>#include<iostream>#include<iterator>int main(int argc,char** argv){
std::vector<std::vector<int>> dest{{1,2,3,4,5},{3,4}};
std::vector<std::vector<int>> src{{6,7,8,9,10}};// Move elements from src to dest.// src is left in undefined but safe-to-destruct state.
dest.insert(
dest.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()));return0;}
Après le déplacement, l'élément de src est laissé dans un état indéfini mais sûr à détruire, et ses anciens éléments ont été transférés directement vers le nouvel élément de dest à la fin.
Ce modèle est utile si les deux vecteurs ne contiennent pas exactement le même type de chose, car vous pouvez utiliser quelque chose au lieu de std :: back_inserter pour convertir d'un type à l'autre.
la méthode de copie n'est pas un si bon moyen. Il appellera push_back plusieurs fois, ce qui signifie que si de nombreux éléments doivent être insérés, cela pourrait signifier plusieurs réallocations. il est préférable d'utiliser l'insertion car l'implémentation vectorielle pourrait faire une certaine optimisation pour éviter les réallocations. il pourrait réserver de la mémoire avant de commencer la copie
Yogesh Arora
7
@Yogesh: d'accord, mais rien ne vous empêche d'appeler en reservepremier. La raison std::copyest parfois utile si vous souhaitez utiliser autre chose que back_inserter.
Roger Lipscombe
Lorsque vous dites "allocations multiples", c'est vrai - mais le nombre d'allocations est au pire journal (nombre d'entrées ajoutées) - ce qui signifie que le coût de l'ajout d'une entrée est constant dans le nombre d'entrées ajoutées. (Fondamentalement, ne vous en faites pas, sauf si le profilage montre que vous avez besoin d'une réserve).
Martin Bonner soutient Monica
Vous voudrez peut-être utiliser std :: transform pour le faire à la place.
Comportement indéfini si a est en fait b (ce qui est OK si vous savez que cela ne peut jamais arriver - mais qu'il vaut la peine d'être conscient du code à usage général).
Martin Bonner soutient Monica
1
@MartinBonner Merci d'avoir mentionné cela. Je devrais probablement revenir à l'ancienne insert, qui est plus sûre.
Deqing
15
Ah, l'AUTRE std :: move. Assez déroutant la première fois que vous le voyez.
xaxxon
1
Est - ce différent de insert()avec move_iterators? Si c'est le cas, comment?
GPhilo
1
J'ai ajouté une note sur ce dont std::movenous parlons ici, car la plupart des gens ne connaissent pas cette surcharge. J'espère que c'est une amélioration.
De plus, ne fait pas partie d'une question, mais il est conseillé de l'utiliser reserveavant de l'ajouter pour de meilleures performances. Et si vous concaténez le vecteur avec lui-même, sans le réserver, il échoue, alors vous devriez toujours le faire reserve.
@Asu ADL n'ajoutera que std::si le type de aprovient std, ce qui vainc l'aspect générique.
Potatoswatter
bon point. dans ce cas, c'est un vecteur, cela fonctionnerait de toute façon, mais oui, c'est une meilleure solution.
Asu
std :: begin () / end () ont été ajoutés pour les collections (comme les tableaux) qui ne les ont pas comme fonctions membres. Mais les tableaux n'ont pas non plus de fonction membre insert () et appellent la question "Existe-t-il une collection avec un insert () mais sans begin () (qui fonctionne avec le std :: begin ())?"
James Curran
15
Avec la gamme v3 , vous pouvez avoir une concaténation paresseuse :
N'est-ce pas la même chose avec la réponse donnée par Tom Ritter et Robert Gamble en 2008?
IgNite
9
Une amélioration générale des performances pour concaténer consiste à vérifier la taille des vecteurs. Et fusionnez / insérez le plus petit avec le plus grand.
Si simple, pourtant je n'y ai jamais pensé de cette façon!
Zimano
2
L'exemple de code est incorrect. v1.insert(v2.end()...utilise un itérateur dans v2pour spécifier la position dans v1.
David Stone
Vous pouvez également utiliser un échange rapide. @DavidStone Je l'ai édité afin que l'ordre de concaturation puisse être changé. Est-il possible d'ajouter au début d'un vecteur?
qwr
Vous pouvez insérer au début, mais ce sera plus lent. Pour vraiment "concaténer", cependant, l'ordre importe généralement, c'est donc ce que vous devez faire.
David Stone
7
Si vous voulez pouvoir concaténer des vecteurs de manière concise, vous pouvez surcharger l' +=opérateur.
Similaire append_moveavec une forte garantie ne peut pas être implémenté en général si le constructeur de mouvement de l'élément vectoriel peut lancer (ce qui est peu probable mais quand même).
L'utilisation T operator+(const T & a, const T & b)est dangereuse, il vaut mieux l'utiliser vector<T> operator+(const vector<T> & a, const vector<T> & b).
Matthieu H
4
Il y a un algorithme std::mergede C ++ 17 , qui est très facile à utiliser,
Je ne pense pas que ce soit plus facile à utiliser std::vector::insert, mais cela fait quelque chose de différent: fusionner deux gammes dans une nouvelle gamme vs insérer un vecteur à la fin d'un autre. Vaut-il la peine de mentionner dans la réponse?
jb
4
Si votre objectif est simplement d'itérer sur la plage de valeurs à des fins de lecture seule, une alternative consiste à enrouler les deux vecteurs autour d'un proxy (O (1)) au lieu de les copier (O (n)), afin qu'ils soient rapidement visibles comme un seul, contigu.
Bien que cet extrait de code puisse résoudre le problème, il n'explique pas pourquoi ni comment il répond à la question. Veuillez inclure une explication pour votre code , car cela aide vraiment à améliorer la qualité de votre message. Flaggers / relecteurs: pour les réponses en code uniquement comme celle-ci, downvote, ne supprimez pas! (Remarque: Cette réponse peut en fait être assez simple pour rendre une explication, et donc des votes négatifs, inutile. Vous pouvez toujours ajouter une explication pour empêcher plus de drapeaux NAA / VLQ.)
Scott Weldon
2
J'ai implémenté cette fonction qui concatène n'importe quel nombre de conteneurs, passant de rvalue-references et copiant autrement
namespaceinternal{// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if// appropriatetemplate<typenameTarget,typenameHead,typename...Tail>voidAppendNoReserve(Target* target,Head&& head,Tail&&... tail){// Currently, require each homogenous inputs. If there is demand, we could probably implement a// version that outputs a vector whose value_type is the common_type of all the containers// passed to it, and call it ConvertingConcatenate.static_assert(
std::is_same_v<typename std::decay_t<Target>::value_type,typename std::decay_t<Head>::value_type>,"Concatenate requires each container passed to it to have the same value_type");ifconstexpr(std::is_lvalue_reference_v<Head>){
std::copy(head.begin(), head.end(), std::back_inserter(*target));}else{
std::move(head.begin(), head.end(), std::back_inserter(*target));}ifconstexpr(sizeof...(Tail)>0){AppendNoReserve(target, std::forward<Tail>(tail)...);}}template<typenameHead,typename...Tail>size_tTotalSize(constHead& head,constTail&... tail){ifconstexpr(sizeof...(Tail)>0){return head.size()+TotalSize(tail...);}else{return head.size();}}}// namespace internal/// Concatenate the provided containers into a single vector. Moves from rvalue references, copies/// otherwise.template<typenameHead,typename...Tail>autoConcatenate(Head&& head,Tail&&... tail){size_t totalSize =internal::TotalSize(head, tail...);
std::vector<typename std::decay_t<Head>::value_type> result;
result.reserve(totalSize);internal::AppendNoReserve(&result, std::forward<Head>(head), std::forward<Tail>(tail)...);return result;}
Si ce que vous cherchez est un moyen d'ajouter un vecteur à un autre après la création, vector::insertc'est votre meilleur pari, comme cela a été répondu plusieurs fois, par exemple:
vector<int> first ={13};const vector<int> second ={42};
first.insert(first.end(), second.cbegin(), second.cend());
Malheureusement, il n'y a aucun moyen de construire un const vector<int>, comme ci-dessus, vous devez construire et ensuite insert.
Si ce que vous recherchez réellement est un conteneur pour contenir la concaténation de ces deux vector<int>s, il peut y avoir quelque chose de mieux à votre disposition, si:
Votre vector contient des primitives
Vos primitives contenues sont de taille 32 bits ou moins
Vous voulez un constconteneur
Si tout ce qui précède est vrai, je vous suggère d'utiliser basic_stringqui char_typecorrespond à la taille de la primitive contenue dans votre vector. Vous devez inclure un static_assertdans votre code pour valider la cohérence de ces tailles:
static_assert(sizeof(char32_t)==sizeof(int));
Avec cette tenue vraie, vous pouvez simplement faire:
Cette solution peut être un peu compliquée, mais elle boost-rangea aussi d'autres belles choses à offrir.
#include<iostream>#include<vector>#include<boost/range/algorithm/copy.hpp>int main(int,char**){
std::vector<int> a ={1,2,3};
std::vector<int> b ={4,5,6};
boost::copy(b, std::back_inserter(a));for(auto& iter : a){
std::cout << iter <<" ";}return EXIT_SUCCESS;}
Souvent, l'intention est de combiner le vecteur aet de bsimplement le répéter en effectuant une opération. Dans ce cas, il y a la joinfonction simple ridicule .
#include<iostream>#include<vector>#include<boost/range/join.hpp>#include<boost/range/algorithm/copy.hpp>int main(int,char**){
std::vector<int> a ={1,2,3};
std::vector<int> b ={4,5,6};
std::vector<int> c ={7,8,9};// Just creates an iteratorfor(auto& iter : boost::join(a, boost::join(b, c))){
std::cout << iter <<" ";}
std::cout <<"\n";// Can also be used to create a copy
std::vector<int> d;
boost::copy(boost::join(a, boost::join(b, c)), std::back_inserter(d));for(auto& iter : d){
std::cout << iter <<" ";}return EXIT_SUCCESS;}
Pour les grands vecteurs, cela peut être un avantage, car il n'y a pas de copie. Il peut également être utilisé pour copier facilement une généralisation dans plusieurs conteneurs.
Pour une raison quelconque, il n'y a rien de tel boost::join(a,b,c), ce qui pourrait être raisonnable.
Vous forboucles avez tort. Les index valides dans un vecteur sont de 0 à size()-1. Vos conditions de résiliation de boucle doivent être i < v1.size(), en utilisant <not <=. Une mauvaise condition permet d'accéder à la mémoire en dehors du conteneur.
Blastfurnace
en plus de ne pas fonctionner, ce code est fortement non idiomatique. Vous devriez au moins utiliser des autoitérateurs au lieu d'une indexation manuelle. Vous ne vous souciez pas de l'index que vous concaténez, mais du fait qu'il est fait de manière séquentielle.
Tarick Welling
Pouvez-vous expliquer pourquoi vous utilisez size()-1dans deux de vos conditions de boucle? Cela ignore les derniers éléments vectoriels. La troisième boucle est la seule correcte maintenant.
Blastfurnace
-3
Pour être honnête, vous pouvez rapidement concaténer deux vecteurs en copiant des éléments de deux vecteurs dans l'autre ou simplement ajouter un seul des deux vecteurs!. Cela dépend de votre objectif.
Méthode 1: attribuer un nouveau vecteur à sa taille est la somme de la taille de deux vecteurs d'origine.
vector<int> concat_vector = vector<int>();
concat_vector.setcapacity(vector_A.size()+ vector_B.size());// Loop for copy elements in two vectors into concat_vector
Méthode 2: ajouter le vecteur A en ajoutant / insérant des éléments du vecteur B.
// Loop for insert elements of vector_B into vector_A with insert() function: vector_A.insert(vector_A .end(), vector_B.cbegin(), vector_B.cend());
Qu'ajoute votre réponse qui n'a pas déjà été fournie dans d'autres réponses?
mat
13
@Mat: caractères gras.
marcv81
Si le ou les vecteurs d'origine ne sont plus nécessaires après, il peut être préférable de les utiliser std::move_iteratorpour que les éléments soient déplacés plutôt que copiés. (voir en.cppreference.com/w/cpp/iterator/move_iterator ).
tmlen
Qu'est-ce que c'est setcapacity? Qu'est-ce que c'est function: ?
a + b
oua.concat(b)
dans la bibliothèque standard? Peut-être que l'implémentation par défaut serait sous-optimale, mais chaque concaténation de tableau n'a pas besoin d'être micro-optimiséeRéponses:
la source
reserve
d'abord le vecteur de destination?vector1.capacity() >= 2 * vector1.size()
. Ce qui est atypique sauf si vous avez appeléstd::vector::reserve()
. Sinon, le vecteur se réallouera, invalidant les itérateurs passés en paramètres 2 et 3..concat
ou+=
ou quelque choseSi vous utilisez C ++ 11 et souhaitez déplacer les éléments plutôt que de simplement les copier, vous pouvez utiliser
std::move_iterator
avec insert (ou copy):Cela ne sera pas plus efficace pour l'exemple avec des entiers, car les déplacer n'est pas plus efficace que les copier, mais pour une structure de données avec des mouvements optimisés, cela peut éviter de copier un état inutile:
Après le déplacement, l'élément de src est laissé dans un état indéfini mais sûr à détruire, et ses anciens éléments ont été transférés directement vers le nouvel élément de dest à la fin.
la source
std::move(src.begin(), src.end(), back_inserter(dest))
?J'utiliserais la fonction d'insertion , quelque chose comme:
la source
Ou vous pouvez utiliser:
Ce modèle est utile si les deux vecteurs ne contiennent pas exactement le même type de chose, car vous pouvez utiliser quelque chose au lieu de std :: back_inserter pour convertir d'un type à l'autre.
la source
reserve
premier. La raisonstd::copy
est parfois utile si vous souhaitez utiliser autre chose queback_inserter
.Avec C ++ 11, je préférerais suivre pour ajouter le vecteur b à a:
quand
a
etb
ne se chevauchent pas, etb
ne sera plus utilisé.C'est
std::move
de<algorithm>
, pas l' habituelstd::move
de<utility>
.la source
insert
, qui est plus sûre.insert()
avecmove_iterator
s? Si c'est le cas, comment?std::move
nous parlons ici, car la plupart des gens ne connaissent pas cette surcharge. J'espère que c'est une amélioration.la source
Je préfère celui qui est déjà mentionné:
Mais si vous utilisez C ++ 11, il existe un autre moyen générique:
De plus, ne fait pas partie d'une question, mais il est conseillé de l'utiliser
reserve
avant de l'ajouter pour de meilleures performances. Et si vous concaténez le vecteur avec lui-même, sans le réserver, il échoue, alors vous devriez toujours le fairereserve
.Donc, fondamentalement, ce dont vous avez besoin:
la source
std::
est déduit par la recherche dépendante de l'argument .end(a)
sera suffisant.std::
si le type dea
provientstd
, ce qui vainc l'aspect générique.Avec la gamme v3 , vous pouvez avoir une concaténation paresseuse :
Démo .
la source
Vous devez utiliser vector :: insert
la source
Une amélioration générale des performances pour concaténer consiste à vérifier la taille des vecteurs. Et fusionnez / insérez le plus petit avec le plus grand.
la source
v1.insert(v2.end()...
utilise un itérateur dansv2
pour spécifier la position dansv1
.Si vous voulez pouvoir concaténer des vecteurs de manière concise, vous pouvez surcharger l'
+=
opérateur.Ensuite, vous pouvez l'appeler comme ceci:
la source
Si vous êtes intéressé par une garantie d'exception forte (lorsque le constructeur de copie peut lever une exception):
Similaire
append_move
avec une forte garantie ne peut pas être implémenté en général si le constructeur de mouvement de l'élément vectoriel peut lancer (ce qui est peu probable mais quand même).la source
v1.erase(...
de lancer aussi?insert
gère déjà cela. De plus, cet appel àerase
est équivalent à aresize
.Ajoutez celui-ci à votre fichier d'en-tête:
et utilisez-le de cette façon:
r contiendra [1,2,62]
la source
Voici une solution à usage général utilisant la sémantique de déplacement C ++ 11:
Notez comment cela diffère de
append
ing à avector
.la source
Vous pouvez préparer votre propre modèle pour l'opérateur +:
La prochaine chose - utilisez simplement +:
Cet exemple donne une sortie:
la source
T operator+(const T & a, const T & b)
est dangereuse, il vaut mieux l'utiliservector<T> operator+(const vector<T> & a, const vector<T> & b)
.Il y a un algorithme
std::merge
de C ++ 17 , qui est très facile à utiliser,Voici l'exemple:
la source
std::vector::insert
, mais cela fait quelque chose de différent: fusionner deux gammes dans une nouvelle gamme vs insérer un vecteur à la fin d'un autre. Vaut-il la peine de mentionner dans la réponse?Si votre objectif est simplement d'itérer sur la plage de valeurs à des fins de lecture seule, une alternative consiste à enrouler les deux vecteurs autour d'un proxy (O (1)) au lieu de les copier (O (n)), afin qu'ils soient rapidement visibles comme un seul, contigu.
Reportez-vous à https://stackoverflow.com/a/55838758/2379625 pour plus de détails, y compris la mise en œuvre de «VecProxy» ainsi que les avantages et les inconvénients.
la source
la source
J'ai implémenté cette fonction qui concatène n'importe quel nombre de conteneurs, passant de rvalue-references et copiant autrement
la source
Si ce que vous cherchez est un moyen d'ajouter un vecteur à un autre après la création,
vector::insert
c'est votre meilleur pari, comme cela a été répondu plusieurs fois, par exemple:Malheureusement, il n'y a aucun moyen de construire un
const vector<int>
, comme ci-dessus, vous devez construire et ensuiteinsert
.Si ce que vous recherchez réellement est un conteneur pour contenir la concaténation de ces deux
vector<int>
s, il peut y avoir quelque chose de mieux à votre disposition, si:vector
contient des primitivesconst
conteneurSi tout ce qui précède est vrai, je vous suggère d'utiliser
basic_string
quichar_type
correspond à la taille de la primitive contenue dans votrevector
. Vous devez inclure unstatic_assert
dans votre code pour valider la cohérence de ces tailles:Avec cette tenue vraie, vous pouvez simplement faire:
Pour plus d'informations sur les différences entre
string
etvector
vous pouvez consulter ici: https://stackoverflow.com/a/35558008/2642059Pour un exemple en direct de ce code, vous pouvez regarder ici: http://ideone.com/7Iww3I
la source
Cette solution peut être un peu compliquée, mais elle
boost-range
a aussi d'autres belles choses à offrir.Souvent, l'intention est de combiner le vecteur
a
et deb
simplement le répéter en effectuant une opération. Dans ce cas, il y a lajoin
fonction simple ridicule .Pour les grands vecteurs, cela peut être un avantage, car il n'y a pas de copie. Il peut également être utilisé pour copier facilement une généralisation dans plusieurs conteneurs.
Pour une raison quelconque, il n'y a rien de tel
boost::join(a,b,c)
, ce qui pourrait être raisonnable.la source
Vous pouvez le faire avec des algorithmes STL pré-implémentés en utilisant un modèle pour une utilisation de type polymorphe.
Vous pouvez effacer le deuxième vecteur si vous ne souhaitez pas l'utiliser davantage (
clear()
méthode).la source
Concatène deux
std::vector-s
avecfor
boucle en unstd::vector
.Exemple:
Écrivez ce code
main()
.la source
for
boucles avez tort. Les index valides dans un vecteur sont de 0 àsize()-1
. Vos conditions de résiliation de boucle doivent êtrei < v1.size()
, en utilisant<
not<=
. Une mauvaise condition permet d'accéder à la mémoire en dehors du conteneur.auto
itérateurs au lieu d'une indexation manuelle. Vous ne vous souciez pas de l'index que vous concaténez, mais du fait qu'il est fait de manière séquentielle.size()-1
dans deux de vos conditions de boucle? Cela ignore les derniers éléments vectoriels. La troisième boucle est la seule correcte maintenant.Pour être honnête, vous pouvez rapidement concaténer deux vecteurs en copiant des éléments de deux vecteurs dans l'autre ou simplement ajouter un seul des deux vecteurs!. Cela dépend de votre objectif.
Méthode 1: attribuer un nouveau vecteur à sa taille est la somme de la taille de deux vecteurs d'origine.
Méthode 2: ajouter le vecteur A en ajoutant / insérant des éléments du vecteur B.
la source
std::move_iterator
pour que les éléments soient déplacés plutôt que copiés. (voir en.cppreference.com/w/cpp/iterator/move_iterator ).setcapacity
? Qu'est-ce que c'estfunction:
?resize
méthode.