Development Tip

두 개의 std :: vector 연결

yourdevel 2020. 10. 2. 23:25
반응형

두 개의 std :: vector 연결


두 개의 std::vectors를 어떻게 연결 합니까?


vector1.insert( vector1.end(), vector2.begin(), vector2.end() );

C ++ 11을 사용하고 있고 단순히 복사하는 것이 아니라 요소를 이동하려는 경우 std::move_iterator삽입 (또는 복사)과 함께 사용할 수 있습니다 .

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

int를 사용하는 예제에서는 이동하는 것이 복사하는 것보다 더 효율적이지 않기 때문에 더 효율적이지 않지만 이동이 최적화 된 데이터 구조의 경우 불필요한 상태 복사를 피할 수 있습니다.

#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의 새 요소로 직접 전송되었습니다.


다음과 같이 삽입 함수를 사용합니다 .

vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());

또는 다음을 사용할 수 있습니다.

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

이 패턴은 두 벡터가 정확히 동일한 유형을 포함하지 않는 경우 유용합니다. std :: back_inserter 대신 무언가를 사용하여 한 유형에서 다른 유형으로 변환 할 수 있기 때문입니다.


C ++ 11에서는 벡터 b를 a에 추가하는 것을 선호합니다.

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

ab중복되지 않고, b더 이상 사용하지 않을.


이다 std::move에서 <algorithm>의하지 보통 std::move 에서 <utility>.


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

first.insert(first.end(), second.begin(), second.end());

이미 언급 된 것을 선호합니다.

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

vector :: insert를 사용해야합니다.

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

범위 v3을 사용하면 지연 연결 이있을 수 있습니다 .

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

데모 .


강력한 예외 보장에 관심이있는 경우 (복사 생성자가 예외를 throw 할 수있는 경우) :

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벡터 요소의 이동 생성자가 던질 수 있으면 일반적으로 강력한 보증과 유사 하게 구현할 수 없습니다 (아마도 가능하지 않지만 여전히).


헤더 파일에 다음을 추가하십시오.

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]를 포함합니다.


vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));

다음은 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);
}

이것이 appending vector.


일반적인 성능 향상 CONCATENATE에 대한이 벡터의 크기를 확인하는 것입니다. 그리고 작은 것을 큰 것과 병합 / 삽입하십시오.

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

+ 연산자에 대한 고유 한 템플릿을 준비 할 수 있습니다.

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;

이 예는 출력을 제공합니다.

 
12 34 5678

벡터를 간결하게 연결하려면 +=연산자를 오버로드 할 수 있습니다.

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;

당신이 찾고있는 것이 생성 후 다른 벡터에 벡터를 추가하는 방법이라면, 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. You want a const container

If the above are all true, I'd suggest using the basic_string who's char_type matches the size of the primitive contained in your vector. You should include a static_assert in your code to validate these sizes stay consistent:

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

With this holding true you can just do:

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

For more information on the differences between string and vector you can look here: https://stackoverflow.com/a/35558008/2642059

For a live example of this code you can look here: http://ideone.com/7Iww3I


This solution might be a bit complicated, but boost-range has also some other nice things to offer.

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

Often ones intention is to combine vector a and b just iterate over it doing some operation. In this case, there is the ridiculous simple join function.

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

For large vectors this might be an advantage, as there is no copying. It can be also used for copying an generalizes easily to more than one container.

For some reason there is nothing like boost::join(a,b,c), which could be reasonable.


I've implemented this function which concatenates any number of containers, moving from rvalue-references and copying otherwise

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

There is an algorithm std::merge from C++17, which is very easy to use,

Below is the example:

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

To be honest, you could fast concatenate two vectors by copy elements from two vectors into the other one or just only append one of two vectors!. It depends on your aim.

Method 1: Assign new vector with its size is the sum of two original vectors' size.

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

Method 2: Append vector A by adding/inserting elements of vector 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());

참고URL : https://stackoverflow.com/questions/201718/concatenating-two-stdvectors

반응형