Comment concaténer deux std::vector
s?
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
Si vous utilisez C++ 11 et souhaitez déplacer les éléments plutôt que de 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 ints, puisque les déplacer n'est pas plus efficace que de les copier, mais pour une structure de données avec des déplacements optimisés, cela peut éviter de copier des états inutiles:
#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 non défini mais sûr à détruire, et ses éléments précédents ont été transférés directement vers le nouvel élément de dest à la fin.
Je voudrais utiliser la fonction insérer , 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 d'éléments, car vous pouvez utiliser quelque chose au lieu de std :: back_inserter pour convertir un type en un autre.
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 moyen générique supplémentaire:
a.insert(std::end(a), std::begin(b), std::end(b));
En outre, cela ne fait pas partie d'une question, mais il est conseillé d'utiliser reserve
avant l'ajout pour améliorer les performances. Et si vous concaténez vector avec lui-même, sans réserver, vous échouerez, vous devriez donc toujours 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());
}
Vous devriez utiliser vector :: insert
v1.insert(v1.end(), v2.begin(), v2.end());
Si vous êtes intéressé par la 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;
}
}
Un append_move
similaire avec une garantie forte ne peut pas être implémenté en général si le constructeur de déplacement de l'élément vectoriel peut lancer (ce qui est peu probable mais quand même).
Un performance générale boost 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{
v1.insert(v2.end(),v1.begin(),v1.end());
}
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
Voici une solution polyvalente 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 en quoi cela diffère de append
ing en un vector
.
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 l'utiliser 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]
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;
Cette solution est peut-être un peu compliquée, mais boost-range
offre également d’autres avantages intéressants.
#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 les vecteurs a
et b
, il suffit de les parcourir pour effectuer certaines opérations. Dans ce cas, il existe la fonction ridicule simple join
.
#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 pourrait être un avantage, car il n'y a pas de copie. Il peut également être utilisé pour copier facilement et généraliser plusieurs conteneurs.
Pour une raison quelconque, rien ne ressemble à boost::join(a,b,c)
, ce qui pourrait être raisonnable.
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;
}
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 la sortie:
1 2 3 4 5 6 7 8
Pour être honnête, vous pouvez concaténer rapidement deux vecteurs en copiant des éléments de deux vecteurs dans l'autre ou simplement en ajoutant l'un des deux vecteurs !. Cela dépend de votre objectif.
Méthode 1: L'affectation d'un nouveau vecteur dont la taille correspond à 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());
Si ce que vous cherchez est un moyen d’ajouter un vecteur à un autre après la création, vector::insert
est votre meilleur choix, comme cela a été répondu à plusieurs reprises, par exemple:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
Malheureusement, il n’ya aucun moyen de construire un const vector<int>
, comme ci-dessus, vous devez construire puis insert
.
Si ce que vous recherchez réellement est un conteneur pour contenir la concaténation de ces deux vector<int>
s, il est possible que quelque chose de mieux soit disponible, si:
vector
contient des primitivesconst
Si toutes les réponses ci-dessus sont vraies, je suggérerais d'utiliser le 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 que ces tailles restent cohérentes:
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
, consultez la page: https://stackoverflow.com/a/35558008/2642059
Pour un exemple en direct de ce code, vous pouvez regarder ici: http://ideone.com/7Iww3I
J'ai implémenté cette fonction qui enchaîne un nombre quelconque de conteneurs, passant des références rvalue à la copie, sinon
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;
}
Il existe un algorithme std::merge
de C++ 17 , 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;
}
_