web-dev-qa-db-fra.com

Concaténation de deux vecteurs std ::

Comment concaténer deux std::vectors?

544
yigal
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
608
Robert Gamble

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.

139
Alex

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());
128
Tom Ritter

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.

74
Roger Lipscombe

Avec C++ 11, je préférerais ensuite ajouter le vecteur b à a:

std::move(b.begin(), b.end(), std::back_inserter(a));

lorsque a et b ne se chevauchent pas et que b ne sera plus utilisé.


Ceci est std::move de <algorithm>, pas le habituelstd::move de <utility>.

42
Deqing
std::vector<int> first;
std::vector<int> second;

first.insert(first.end(), second.begin(), second.end());
31
James Curran

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());
}
22
ST3

Vous devriez utiliser vector :: insert

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

Avec range v3 , vous pouvez avoir une concaténation lazy:

ranges::view::concat(v1, v2)

Démo .

8
Jarod42

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).

4
AlexT

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());
}
3
Vikramjit Roy
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
3
instance

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.

3
Daniel

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]

2
Stepan Yakovenko

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;
1
Daniel Giger

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.

1
Aleph0

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
0
Vladimir U.

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());
0
nvnhcmus

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:

  1. Votre vector contient des primitives
  2. Vos primitives contenues sont de taille 32 bits ou inférieure
  3. Vous voulez un conteneur const

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

0
Jonathan Mee

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;
}
0
Drew

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;
}
_
0
Pavan Chandaka