Comment concaténer deux std::vector
s?
a + b
ou 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
Comment concaténer deux std::vector
s?
a + b
ou 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
Réponses:
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
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 chose
Si 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):
#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")
);
return 0;
}
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())
);
return 0;
}
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.
std::move(src.begin(), src.end(), back_inserter(dest))
?
J'utiliserais la fonction d'insertion , quelque chose comme:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Ou vous pouvez utiliser:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
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.
reserve
premier. La raison std::copy
est parfois utile si vous souhaitez utiliser autre chose que back_inserter
.
Avec C ++ 11, je préférerais suivre pour ajouter le vecteur b à a:
std::move(b.begin(), b.end(), std::back_inserter(a));
quand a
et b
ne se chevauchent pas, et b
ne sera plus utilisé.
C'est std::move
de <algorithm>
, pas l' habituel std::move
de <utility>
.
insert
, qui est plus sûre.
insert()
avec move_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.
std::vector<int> first;
std::vector<int> second;
first.insert(first.end(), second.begin(), second.end());
Je préfère celui qui est déjà mentionné:
a.insert(a.end(), b.begin(), b.end());
Mais si vous utilisez C ++ 11, il existe un autre moyen générique:
a.insert(std::end(a), std::begin(b), std::end(b));
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 faire reserve
.
Donc, fondamentalement, ce dont vous avez besoin:
template <typename T>
void Append(std::vector<T>& a, const std::vector<T>& b)
{
a.reserve(a.size() + b.size());
a.insert(a.end(), b.begin(), b.end());
}
std::
si le type de a
provient std
, ce qui vainc l'aspect générique.
Vous devez utiliser vector :: insert
v1.insert(v1.end(), v2.begin(), v2.end());
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.
//vector<int> v1,v2;
if(v1.size()>v2.size()) {
v1.insert(v1.end(),v2.begin(),v2.end());
} else {
v2.insert(v2.end(),v1.begin(),v1.end());
}
v1.insert(v2.end()...
utilise un itérateur dans v2
pour spécifier la position dans v1
.
Si vous voulez pouvoir concaténer des vecteurs de manière concise, vous pouvez surcharger l' +=
opérateur.
template <typename T>
std::vector<T>& operator +=(std::vector<T>& vector1, const std::vector<T>& vector2) {
vector1.insert(vector1.end(), vector2.begin(), vector2.end());
return vector1;
}
Ensuite, vous pouvez l'appeler comme ceci:
vector1 += vector2;
Si vous êtes intéressé par une garantie d'exception forte (lorsque le constructeur de copie peut lever une exception):
template<typename T>
inline void append_copy(std::vector<T>& v1, const std::vector<T>& v2)
{
const auto orig_v1_size = v1.size();
v1.reserve(orig_v1_size + v2.size());
try
{
v1.insert(v1.end(), v2.begin(), v2.end());
}
catch(...)
{
v1.erase(v1.begin() + orig_v1_size, v1.end());
throw;
}
}
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).
v1.erase(...
de lancer aussi?
insert
gère déjà cela. De plus, cet appel à erase
est équivalent à a resize
.
Ajoutez celui-ci à votre fichier d'en-tête:
template <typename T> vector<T> concat(vector<T> &a, vector<T> &b) {
vector<T> ret = vector<T>();
copy(a.begin(), a.end(), back_inserter(ret));
copy(b.begin(), b.end(), back_inserter(ret));
return ret;
}
et utilisez-le de cette façon:
vector<int> a = vector<int>();
vector<int> b = vector<int>();
a.push_back(1);
a.push_back(2);
b.push_back(62);
vector<int> r = concat(a, b);
r contiendra [1,2,62]
Voici une solution à usage général utilisant la sémantique de déplacement C ++ 11:
template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
if (lhs.empty()) return rhs;
if (rhs.empty()) return lhs;
std::vector<T> result {};
result.reserve(lhs.size() + rhs.size());
result.insert(result.cend(), lhs.cbegin(), lhs.cend());
result.insert(result.cend(), rhs.cbegin(), rhs.cend());
return result;
}
template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, const std::vector<T>& rhs)
{
lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
return std::move(lhs);
}
template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, std::vector<T>&& rhs)
{
rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
return std::move(rhs);
}
template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, std::vector<T>&& rhs)
{
if (lhs.empty()) return std::move(rhs);
lhs.insert(lhs.cend(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return std::move(lhs);
}
Notez comment cela diffère de append
ing à a vector
.
Vous pouvez préparer votre propre modèle pour l'opérateur +:
template <typename T>
inline T operator+(const T & a, const T & b)
{
T res = a;
res.insert(res.end(), b.begin(), b.end());
return res;
}
La prochaine chose - utilisez simplement +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Cet exemple donne une sortie:
1 2 3 4 5 6 7 8
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)
.
Il y a un algorithme std::merge
de C ++ 17 , qui est très facile à utiliser,
Voici l'exemple:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
//DATA
std::vector<int> v1{2,4,6,8};
std::vector<int> v2{12,14,16,18};
//MERGE
std::vector<int> dst;
std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));
//PRINT
for(auto item:dst)
std::cout<<item<<" ";
return 0;
}
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.
std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };
VecProxy<int> AB(A, B); // ----> O(1)!
for (size_t i = 0; i < AB.size(); i++)
std::cout << AB[i] << " "; // ----> 1 2 3 4 5 10 20 30
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.
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
J'ai implémenté cette fonction qui concatène n'importe quel nombre de conteneurs, passant de rvalue-references et copiant autrement
namespace internal {
// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if
// appropriate
template<typename Target, typename Head, typename... Tail>
void AppendNoReserve(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");
if constexpr (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));
}
if constexpr (sizeof...(Tail) > 0) {
AppendNoReserve(target, std::forward<Tail>(tail)...);
}
}
template<typename Head, typename... Tail>
size_t TotalSize(const Head& head, const Tail&... tail) {
if constexpr (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<typename Head, typename... Tail>
auto Concatenate(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::insert
c'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:
vector
contient des primitivesconst
conteneurSi tout ce qui précède est vrai, je vous suggère d'utiliser basic_string
qui char_type
correspond à la taille de la primitive contenue dans votre vector
. Vous devez inclure un static_assert
dans 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:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Pour plus d'informations sur les différences entre string
et vector
vous pouvez consulter ici: https://stackoverflow.com/a/35558008/2642059
Pour un exemple en direct de ce code, vous pouvez regarder ici: http://ideone.com/7Iww3I
Cette solution peut être un peu compliquée, mais elle boost-range
a 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 a
et de b
simplement le répéter en effectuant une opération. Dans ce cas, il y a la join
fonction 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 iterator
for (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 pouvez le faire avec des algorithmes STL pré-implémentés en utilisant un modèle pour une utilisation de type polymorphe.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T>
void concat(std::vector<T>& valuesa, std::vector<T>& valuesb){
for_each(valuesb.begin(), valuesb.end(), [&](int value){ valuesa.push_back(value);});
}
int main()
{
std::vector<int> values_p={1,2,3,4,5};
std::vector<int> values_s={6,7};
concat(values_p, values_s);
for(auto& it : values_p){
std::cout<<it<<std::endl;
}
return 0;
}
Vous pouvez effacer le deuxième vecteur si vous ne souhaitez pas l'utiliser davantage ( clear()
méthode).
Concatène deux std::vector-s
avec for
boucle en un std::vector
.
Exemple:
std::vector <int> v1 {1, 2, 3};//declare vector1
std::vector <int> v2 {4, 5};//declare vector2
std::vector <int> suma;//declare vector suma
for(int i = 0; i < v1.size();i++)//for loop 1
{
suma.push_back(v1[i]);
}
for(int i = 0; i< v2.size();i++)/for loop 2
{
suma.push_back(v2[i]);
}
for(int i = 0; i < suma.size(); i++)//for loop 3-output
{
std::cout<<suma[i];
}
Écrivez ce code main()
.
for
boucles 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.
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.
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());
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'est function:
?
resize
méthode.