创建const std::vector作为两个const std::vector的连接



我想创建一个const std::vector来包含其他两个相同类型的const std::vector的所有元素。由于向量是const,我不能使用连接两个std::向量中提到的方法将它与两个const std::vector逐步连接。

#include <iostream>
#include <vector>
int main()
{
const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
const std::vector<int> all_ints;

for (int i: all_ints)
std::cout << i << ' ';
return 0;
}

对于上面的例子,我想以输出为0 1 2 3的方式定义all_ints

这怎么可能呢?

创建一个函数,获取另外两个向量,创建第三个向量,插入前两个向量的值,按值返回结果。然后将其赋值给const vector:

const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
const std::vector<int> all_ints = concat(int_a, int_b);

我真的不知道像这样创建const向量的本质是什么。但一个简单的方法是创建一个临时的非const向量,用前两个向量填充它,然后创建最终的const向量。如:

const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
std::vector<int> middle(int_a);
middle.insert(middle.begin(),int_b.begin(),int_b.end());
const std::vector<int> all_ints(middle);

如注释所示,最后一行可以写成:

const std::vector<int> all_ints = std::move(middle);

正如在@Ayxan Haqverdili的回答中已经提到的,您可以创建一个连接函数,该函数将用于初始化vector。

我建议这样一个函数的实现如下:

template <template <typename, typename> typename C, typename ... Args>
C<Args...> concat(const C<Args...> & lhs, const C<Args...> & rhs)
{
C<Args...> res(lhs.cbegin(), lhs.cend());
res.insert(res.cend(), rhs.cbegin(), rhs.cend());
return res;
}

注意:此实现可推广到除std::array以外的所有标准库序列容器。

可以这样使用:

const std::vector<int> a {1, 2, 3};
const std::vector<int> b {4, 5};

const std::vector<int> ab = concat(a, b);

这里有一个实例


另一个更简单的版本可以是:

template <typename C>
C concat(const C & lhs, const C & rhs)
{
C res(lhs.size() + rhs.size());
typename C::iterator it = std::copy(lhs.cbegin(), lhs.cend(), res.begin());
std::copy(rhs.cbegin(), rhs.cend(), it);
return res;
}

这里有一个实例

这里还有另一个实现,可以很容易地修改,甚至可以在将来修改all_ints的内容。它不需要最新的c++版本。

#include <vector>
#include <algorithm>
int main()
{
const std::vector<int> int_a{ 0,1 };
const std::vector<int> int_b{ 2,3 };
const std::vector<int> all_ints(int_a.size()+ int_b.size());
std::vector<int>& all_intsr = const_cast<std::vector<int>&>(all_ints);
std::copy(int_b.begin(), int_b.end(), std::copy(int_a.begin(), int_a.end(), all_intsr.begin()));
}

这利用了能够合法地将const vector&转换为vector的优势,并具有以下限制以防止UB。不能修改矢量对象。这并不包括vector所拥有的非const类型的内容。begin()end()都不修改vector对象。以后修改它的元素也是合法的,例如:

all_intsr[3]=42;

如果您只是想遍历两个向量,您可以使用自定义视图连接器来实现。下面的代码比创建(和销毁)另一个容器来遍历两个范围要有效得多。

#include <iostream>
#include <array>
#include <ranges>
#include <vector>
#include <utility>
template<typename T1, typename T2>
auto concat_view(T1& lhs, T2& rhs)
{
static_assert(std::is_same_v<std::decay_t<T1>, std::decay_t<T2>>);
using T1_ = std::remove_reference_t<T1>;
using T2_ = std::remove_reference_t<T2>;
if constexpr (std::is_const_v<T1_> || std::is_const_v<T2_>)
{
using Iter = typename std::decay_t<T1>::const_iterator;
return std::array<std::ranges::subrange<Iter>, 2>{std::as_const(lhs), std::as_const(rhs)} | std::views::join;
}
else
{
using Iter = typename std::decay_t<T1>::iterator;
return std::array<std::ranges::subrange<Iter>, 2>{lhs, rhs} | std::views::join;
}    
}
int main()
{
std::vector<int> v1{1,2,3}, v2{4,5,6};
for (int& val : concat_view(v1, v2))
++val;
for (const auto& val : concat_view(std::as_const(v1), v2))
std::cout << val << 'n';
return 0;
}

最新更新