簡體   English   中英

連接兩個 std::vectors

[英]Concatenating two std::vectors

如何連接兩個std::vector

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

對於帶有整數的示例,這不會更有效,因為移動它們並不比復制它們更有效,但是對於具有優化移動的數據結構,它可以避免復制不必要的狀態:

#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


這是來自<algorithm>的 std:: std::move ,而不是來自<utility>通常std::move

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以獲得更好的性能。 而且,如果您將 vector 與其自身連接,而不保留它會失敗,因此您始終應該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());
}

使用range v3 ,您可能會有一個惰性連接:

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

演示

連接的一般性能提升是檢查向量的大小。 並將較小的與較大的合並/插入。

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

如果您希望能夠簡潔地連接向量,您可以重載+=運算符。

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;

有一個來自C++17的算法std::merge ,在對輸入向量進行排序時非常容易使用,

下面是示例:

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

如果您對強異常保證感興趣(當復制構造函數可以拋出異常時):

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

你應該使用vector::insert

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

如果您的目標只是為了只讀目的而迭代值的范圍,另一種方法是將兩個向量包裝在代理周圍(O(1))而不是復制它們(O(n)),以便立即看到它們作為一個單一的,連續的。

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

有關更多詳細信息,包括“VecProxy”實現以及優缺點,請參閱https://stackoverflow.com/a/55838758/2379625

將此添加到您的頭文件中:

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]

這是使用 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);
}

請注意這與appendvector有何不同。

您可以為 + 運算符准備自己的模板:

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

我已經實現了這個函數,它連接任意數量的容器,從右值引用移動並以其他方式復制

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

使用 C++20,您可以使用范圍擺脫 begin() 和 end()。

#include <ranges>

std::ranges::copy(vec2, std::back_inserter(vec1));

或者如果你想移動元素:

std::ranges::move(vec2, std::back_inserter(vec1));

這個解決方案可能有點復雜,但是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;
}

通常一個意圖是結合向量ab只是迭代它做一些操作。 在這種情況下,有可笑的簡單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)這樣的,這可能是合理的。

對於提供push_back (string, vector, deque, ...) 的容器:

std::copy(std::begin(input), std::end(input), std::back_inserter(output))

對於提供insert (地圖、集合)的容器:

std::copy(std::begin(input), std::end(input), std::inserter(output, output.end()))

如果您正在尋找一種在創建后將向量附加到另一個向量的方法,那么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容器

如果以上都是真的,我建議使用char_type與你的vector中包含的基元的大小相匹配的basic_string 您應該在代碼中包含一個static_assert以驗證這些大小是否保持一致:

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

有了這一點,你可以這樣做:

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

有關stringvector之間差異的更多信息,您可以在這里查看: https ://stackoverflow.com/a/35558008/2642059

有關此代碼的實時示例,您可以在此處查看:http: //ideone.com/7Iww3I

您可以使用模板使用多態類型使用預先實現的 STL 算法來完成此操作。

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

如果您不想進一步使用它,可以清除第二個向量( clear()方法)。

在一個std::vector中連接兩個std::vector-sfor循環。

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

老實說,您可以通過將兩個向量中的元素復制到另一個向量中來快速連接兩個向量,或者只附加兩個向量中的一個! 這取決於你的目標。

方法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:通過添加/插入向量B的元素來附加向量A。

// 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::vector<int> v1{1,2,3};
std::vector<int> v2{4,5};

for(int i = 0; i<v2.size();i++)
{
     v1.push_back(v2[i]);
}

v1:1,2,3。

描述:

當 i int 不是 v2 大小時,推回元素,索引 i 在 v1 向量中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM