连接两个std::vectors

988

我应该如何拼接两个std::vector


10
给出的答案实际上并没有连接。它们附加了一个副本。也许创建一个std::vector连接方法在效率方面有用,但这将需要对节点管理进行一些复杂的共享,这可能就是为什么它还没有被实现的原因。 - Douglas Daseeco
16
@FauChristian:从效率的角度来看,可能没有用处。向量内存必须是连续的,所以你建议的做法是不可能的。如果你希望进行“一些复杂的节点管理共享”,并且你要以这种方式更改向量类,那么你最终会得到一个deque。即使这样,按照建议的方式重用内存也非常困难,尽管这样开始变得更加可行。我认为它目前还没有被实现。主要问题是,在这种节点管理共享(deque)中,末尾节点可能部分为空。 - Cookie
17
我是唯一一个想知道为什么标准库中没有将这个实现为 a + ba.concat(b) 的人吗?也许默认实现会不够优化,但并不需要对每个数组连接都进行微观优化。 - oseiskar
54
多年的演化,成为任何主流语言中最先进的运算符重载系统,采用一种模板系统来增加语言的复杂性,然而答案并不是简单的 v = v1 + v2; - Spike0xff
7
我的猜测是,STL不想在语言方面过于具体,以防您希望将运算符用于其他操作,比如在物理模型中添加力向量。在这种情况下,您可能希望重载 forceVector1 + forceVector2,以便在清晰简明的代码中进行逐项加法。 - Jonathan Lidbeck
显示剩余2条评论
29个回答

3

我实现了这个函数,它可以连接任意数量的容器,从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;
}

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

6
这段代码可能解决了问题,但它并没有解释为什么或者如何回答这个问题。请在你的代码中包含一份解释,因为这真的有助于提高你的帖子质量。 标记者/审阅员: 对于类似这样的仅包含代码的答案,请进行投票下降,而不是删除!(注意:这个答案可能实际上简单到不需要解释,因此可以不必 downvotes。你仍然可能想要添加一个解释来防止更多的 NAA/VLQ 标志。) - Scott Weldon

1

这个解决方案可能有点复杂,但是 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)这样的东西,这可能是合理的。

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

关于 stringvector 之间的区别,您可以在这里查看更多信息:https://dev59.com/jW455IYBdhLWcg3wD_6Z#35558008

如果您需要此代码的实时示例,请参见此处:http://ideone.com/7Iww3I


0

我尝试使用C++17解决这个任务,而不使用rangesV3库。在阅读了一些关于此主题的帖子后,我得出了这个解决方案:

namespace algorithms
{
/*!
 * \brief Wraps incoming element and move/or copy the elements from one to another.
 * \example return (wrapped_plus(container) + ...).value
 */
template <class T>
struct wrapped_plus {
    using value_type = T;

    value_type value;

    wrapped_plus(value_type&& in) : value(std::move(in)) {}
    wrapped_plus(const value_type& in) : value(in) {}

    wrapped_plus operator+(const wrapped_plus& in)
    {
        std::copy(std::begin(in.value), std::end(in.value), std::back_inserter(value));
        return *this;
    }

    wrapped_plus operator+(wrapped_plus&& in)
    {
        std::move(std::make_move_iterator(std::begin(in.value)), std::make_move_iterator(std::end(in.value)), std::back_inserter(value));
        return *this;
    }
};

/*!
 * \brief Merge 2 or more STL containers with same type.
 * \example merge(container,container,container)
 */
template <typename... Containers>
static inline auto merge(Containers&&... c)
{
    return (wrapped_plus(c) + ...).value;
}
}  // namespace algorithms

int main(int argc, char** argv)
{
    std::vector<int> dest{1,2,3,4,5};
    std::vector<int> src{6,7,8,9,10};

    auto result =  algorithms::merge(dest,src);

    return 0;
}

完整示例


0

您可以使用预先实现的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()方法来清除它。

-2

使用 for 循环将两个 std::vector 连接成一个 std::vector

    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
除了它不能工作之外,这段代码非常不符合惯用法。你应该至少使用“auto”迭代器而不是手动索引。你并不关心你正在连接的索引,只关心它是按顺序完成的。 - Tarick Welling
@TarickWelling,我不明白为什么你说这段代码不起作用,你能具体说明一下吗? - user6547518
7
你有没有检查我的评论的日期?你现在修复了你的代码中的漏洞,但它并不符合惯用语。 - Tarick Welling

-5
说实话,你可以通过将两个向量的元素复制到另一个向量中或仅附加其中一个向量来快速连接两个向量!这取决于你的目的。
方法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());

4
你的回答有什么不同于其他回答提供的内容? - Mat
18
@Mat:粗体字。 - marcv81
如果原始向量在之后不再需要,最好使用std::move_iterator,以便移动元素而不是复制它们。 (请参阅http://en.cppreference.com/w/cpp/iterator/move_iterator)。 - tmlen
1
什么是 setcapacity?什么是 function: - L. F.
@L.F. 我认为他在谈论 resize 方法。 - Matthieu H

-6

尝试创建两个向量并将第二个向量添加到第一个向量中,代码:

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不等于v2的大小时,在v1向量中将元素插入到索引为i的位置。


1
你的描述不清楚(而且无用,抱歉)。否则,如果您添加第二个解决方案将连接保存在另一个向量中而不是修改其中一个,则您的答案可能更完整。 - user6547518

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接