Объединение двух стандартных: векторов

c++ vector stl concatenation stdvector

309596 просмотра

21 ответа

Как мне соединить два std::vectorс?

Источник Размещён: 22.08.2019 06:29

Ответы (21)


648 плюса

vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
Автор: Robert Gamble Размещён: 14.10.2008 03:48

160 плюса

Если вы используете C ++ 11 и хотите перемещать элементы, а не просто копировать их, вы можете использовать std::move_iteratorвместе со insert (или 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;
}

Это не будет более эффективным для примера с целочисленными значениями, поскольку перемещение их не более эффективно, чем их копирование, но для структуры данных с оптимизированными перемещениями можно избежать копирования ненужного состояния:

#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;
}

После перемещения элемент src остается в неопределенном, но безопасном для уничтожения состоянии, а его прежние элементы передаются непосредственно новому элементу dest в конце.

Автор: Alex Размещён: 23.02.2014 06:11

131 плюса

Я бы использовал функцию вставки , что-то вроде:

vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Автор: Tom Ritter Размещён: 14.10.2008 03:48

75 плюса

Или вы можете использовать:

std::copy(source.begin(), source.end(), std::back_inserter(destination));

Этот шаблон полезен, если два вектора не содержат абсолютно одинаковую вещь, потому что вы можете использовать что-то вместо std :: back_inserter для преобразования из одного типа в другой.

Автор: Roger Lipscombe Размещён: 14.10.2008 04:20

51 плюса

В C ++ 11 я бы предпочел добавить вектор b к a:

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

когда aи bне перекрываются, и bбольше не будут использоваться.


Это std::moveиз <algorithm>, а не обычный std::move с <utility>.

Автор: Deqing Размещён: 12.06.2015 07:53

31 плюса

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

first.insert(first.end(), second.begin(), second.end());
Автор: James Curran Размещён: 14.10.2008 03:48

22 плюса

Я предпочитаю тот, который уже упоминался:

a.insert(a.end(), b.begin(), b.end());

Но если вы используете C ++ 11, есть еще один общий способ:

a.insert(std::end(a), std::begin(b), std::end(b));

Кроме того, не является частью вопроса, но его желательно использовать reserveперед добавлением для повышения производительности. И если вы соединяете вектор с самим собой, без резервирования он не удастся, так что вам всегда следует reserve.


Итак, в основном то, что вам нужно:

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());
}
Автор: ST3 Размещён: 10.06.2016 09:27

13 плюса

Вы должны использовать vector :: insert

v1.insert(v1.end(), v2.begin(), v2.end());
Автор: Boris Размещён: 08.08.2017 09:38

10 плюса

С диапазоном v3 у вас может быть ленивая конкатенация:

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

Demo .

Автор: Jarod42 Размещён: 08.08.2017 08:49

4 плюса

Если вы заинтересованы в строгой гарантии исключения (когда конструктор копирования может выдать исключение):

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

Подобное append_moveсо строгой гарантией не может быть реализовано вообще, если конструктор перемещения векторного элемента может бросить (что маловероятно, но все же).

Автор: AlexT Размещён: 06.05.2014 08:21

4 плюса

Общий прирост производительности для СЦЕПИТЬ, чтобы проверить размер векторов. И объединить / вставить меньший с большим.

//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());
}
Автор: Vikramjit Roy Размещён: 08.05.2018 10:11

3 плюса

Добавьте это в свой заголовочный файл:

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

и используйте это так:

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 будет содержать [1,2,62]

Автор: Stepan Yakovenko Размещён: 11.11.2015 11:32

3 плюса

vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
Автор: instance Размещён: 16.12.2016 11:57

3 плюса

Вот решение общего назначения с использованием семантики перемещения 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);
}

Обратите внимание, как это отличается от appendв vector.

Автор: Daniel Размещён: 08.03.2018 01:48

1 плюс

Вы можете подготовить свой собственный шаблон для оператора +:

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

Следующая вещь - просто используйте +:

vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
    cout << x << " ";
cout << endl;

Этот пример дает вывод:

1 2 3 4 5 6 7 8
Автор: Vladimir U. Размещён: 31.07.2018 12:43

1 плюс

Если вы хотите иметь возможность краткого объединения векторов, вы можете перегрузить +=оператор.

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

Тогда вы можете назвать это так:

vector1 += vector2;
Автор: Daniel Giger Размещён: 26.06.2019 10:15

0 плюса

Если то, что вы ищете, - это способ добавить вектор к другому после создания, то vector::insertэто ваша лучшая ставка, на которую уже отвечали несколько раз, например:

vector<int> first = {13};
const vector<int> second = {42};

first.insert(first.end(), second.cbegin(), second.cend());

К сожалению, нет способа построить const vector<int>, как выше, вы должны построить, а затем insert.


Если то, что вы на самом деле ищете, является контейнером для объединения этих двух элементов vector<int>, вам может быть доступно что-то лучше, если:

  1. Ваш vectorсодержит примитивы
  2. Ваши содержащиеся примитивы имеют размер 32 бита или меньше
  3. Вы хотите constконтейнер

Если все вышеприведенное верно, я бы предложил использовать тот, basic_stringкто char_typeсоответствует размеру примитива, содержащегося в вашем vector. Вы должны включить static_assertв свой код, чтобы подтвердить соответствие этих размеров:

static_assert(sizeof(char32_t) == sizeof(int));

С этим подтверждением вы можете просто сделать:

const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());

Для получения дополнительной информации о различиях между stringи vectorвы можете посмотреть здесь: https://stackoverflow.com/a/35558008/2642059

Живой пример этого кода вы можете посмотреть здесь: http://ideone.com/7Iww3I

Автор: Jonathan Mee Размещён: 22.02.2016 06:53

0 плюса

Это решение может быть немного сложным, но boost-rangeесть и другие приятные предложения.

#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;
}

Часто намерение состоит в том, чтобы объединить вектор aи bпросто перебрать его, выполнив некоторую операцию. В этом случае есть смешная простая 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;
}

Для больших векторов это может быть преимуществом, так как нет копирования. Его также можно использовать для простого копирования обобщений в несколько контейнеров.

По какой-то причине нет ничего подобного boost::join(a,b,c), что может быть разумным.

Автор: Aleph0 Размещён: 06.12.2018 01:44

0 плюса

Я реализовал эту функцию, которая объединяет любое количество контейнеров, переходя от rvalue-ссылок и копируя иначе

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;
}
Автор: Drew Размещён: 11.07.2019 09:10

0 плюса

Есть алгоритм std::mergeиз C ++ 17 , который очень прост в использовании,

Ниже приведен пример:

#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;
}
Автор: Pavan Chandaka Размещён: 17.08.2019 05:53

-1 плюса

Если честно, вы можете быстро объединить два вектора, скопировав элементы из двух векторов в другой или просто добавив только один из двух векторов !. Это зависит от вашей цели.

Способ 1: назначить новый вектор с его размером является суммой размеров двух исходных векторов.

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

Способ 2: добавить вектор A, добавив / вставив элементы вектора 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());
Автор: nvnhcmus Размещён: 27.12.2016 06:39
32x32