连接两个标准::向量
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;
}
对于使用 ints 的示例,这不会更有效,因为移动它们并不比复制它们更有效,但对于具有优化移动的数据结构,它可以避免复制不必要的状态:
#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));
当a
和b
不再重叠时,b
将不再使用。
这是从<algorithm>
std::move
,而不是通常的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());
}
对于范围 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());
}
有一个从 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>
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
v1.insert(v1.end(), v2.begin(), v2.end());
如果您对强异常保证感兴趣(当复制构造函数可以抛出异常时(:
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
(这不太可能,但仍然如此(。
如果您的目标只是出于只读目的遍历值范围,另一种方法是将两个向量包装在代理 (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
有关更多详细信息,请参阅 https://stackoverflow.com/a/55838758/2379625,包括"VecProxy"实现以及优缺点。
使用 C++20,你可以用范围去掉 begin(( 和 end((。
#include <ranges>
std::ranges::copy(vec2, std::back_inserter(vec1));
或者,如果要移动元素:
std::ranges::move(vec2, std::back_inserter(vec1));
将这个添加到你的头文件中:
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++23 中,可以使用其成员函数 append_range
将范围连接到标准容器(如果存在(。
然后,可以按如下方式连接两个std::vector
:
vec1.append_range(vec2);
下面是使用 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
有何不同。
您可以为 + 运算符准备自己的模板:
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;
}
对于提供push_back
(字符串、向量、双端面等(的容器:
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()))
这个解决方案可能有点复杂,但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)
更合理的了。
如果您正在寻找一种在创建后将向量附加到另一个向量的方法,那么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>
的串联,那么在以下情况下,您可能会有更好的选择:
- 您的
vector
包含基元 - 包含的基元大小为 32 位或更小
- 你想要一个
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());
有关string
和vector
之间差异的更多信息,您可以查看此处: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()
方法(。
我试图在不使用rangesV3库的情况下在C++17中解决此任务。在阅读了本主题中的一些帖子后,我制定了以下解决方案:
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;
}
完整示例
std::vector
中连接两个std::vector-s
for
循环。
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 大小,但推回元素,在 v1 向量中索引 i。
- 标准::向量声明中使用的模板参数
- 如何声明一个标准::提升直方图的向量?提升直方图的类型是什么?
- 使用 C++98 标准填充二维静态向量
- 重载运算符 + 用于向量:命名空间标准
- 如何将整数向量插入到键中,标准::映射的值
- 在使用标准向量函数时引发'std::bad_alloc'实例后调用的终止
- 在标准向量中将元素分配给 Eigen::Vector2d 会引发错误
- 标准向量之上的 C++11 包装类
- 标准::向量的协变包装器
- 标准::向量不更改类内的数据
- 如何从标准向量前端读取并删除读取变量
- 标准::向量和构造函数
- 标准::向量转换为cl_float
- swig,从列表到非标准向量
- 特征和标准::向量
- 从标准向量到特征/密集向量的矩阵
- 不能在标准向量中插入自己的结构,但是字符串可以
- 标准::向量插入的摊销分析
- C++标准::向量模板与插入
- 连接两个标准::向量