关于C++:连接两个std :: vector

Concatenating two std::vectors

如何连接两个std::vectors?


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


如果您正在使用C++ 11,并且希望移动元素而不是仅仅复制它们,则可以使用EDCOX1与0(插入)(或复制)一起使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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,"
"
)
    );

  return 0;
}

对于带有ints的示例,这不会更有效,因为移动它们并不比复制它们更有效,但是对于具有优化移动的数据结构,它可以避免复制不必要的状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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的新元素。


我将使用insert函数,比如:

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

或者你可以使用:

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

如果两个向量不包含完全相同的类型,则此模式非常有用,因为您可以使用某些内容而不是std::back_inserter从一种类型转换为另一种类型。


用C++ 11,我更喜欢追加向量B到A:

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

ab不重叠,b不再使用时。

这是来自std::move,而不是通常来自std::move


1
2
3
4
std::vector<int> first;
std::vector<int> second;

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


我喜欢前面提到的那种:

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

但是如果使用C++ 11,则有一种更通用的方式:

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

此外,这不是问题的一部分,但建议在附加之前使用reserve,以获得更好的性能。如果您将向量与它本身连接起来,而不保留它失败,那么您总是应该使用reserve

所以基本上你需要的是:

1
2
3
4
5
6
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

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

对于范围v3,您可能有一个延迟的连接:

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

演示。


连接的一般性能提升是检查向量的大小。并将较小的和较大的合并/插入。

1
2
3
4
5
6
//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());
}


如果您对强异常保证感兴趣(当复制构造函数可以抛出异常时):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
    }
}

如果向量元素的move构造器可以抛出(虽然不太可能,但仍然如此),那么一般来说,具有强保证的类似append_move不能实现。


将此文件添加到头文件:

1
2
3
4
5
6
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;
}

用这种方法:

1
2
3
4
5
6
7
8
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]


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


这是一个使用C++ 11移动语义的通用解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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有什么不同。


这个解决方案可能有点复杂,但是boost-range还有一些其他的好东西可以提供。

1
2
3
4
5
6
7
8
9
10
11
12
13
#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函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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 <<"
"
;
    // 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)那样合理。


您可以为+运算符准备自己的模板:

1
2
3
4
5
6
7
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;
}

接下来-只需使用+:

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

这个例子给出了输出:

1
<blockquote>1 2 3 4 5 6 7 8</blockquote>


老实说,您可以通过将元素从两个向量复制到另一个向量来快速连接两个向量,或者只附加两个向量中的一个!这取决于你的目标。

方法1:指定新的向量,其大小是两个原始向量大小的和。

1
2
3
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。

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


如果您要寻找的是在创建后将向量附加到另一个向量的方法,那么vector::insert是您的最佳选择,正如已经多次回答的那样,例如:

1
2
3
4
vector<int> first = {13};
const vector<int> second = {42};

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

遗憾的是,没有办法构建一个const vector,正如上面所述,您必须先构建,然后再构建insert

如果您实际需要的是一个容器来容纳这两个vectors的串联,那么您可能会有更好的选择,如果:

  • 您的vector包含原语
  • 包含的原语大小为32位或更小
  • 你想要一个const集装箱
  • 如果以上都是正确的,我建议使用basic_string,谁是char_type,与你的vector中包含的原语大小相匹配。您应该在代码中包含一个static_assert,以验证这些大小是否保持一致:

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

    有了这一点,你就可以做到:

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

    有关stringvector之间差异的更多信息,请访问:https://stackoverflow.com/a/35558008/2642059

    有关此代码的实况示例,请参阅:http://ideone.com/7iw3i