C++,复制集到向量

Posted

技术标签:

【中文标题】C++,复制集到向量【英文标题】:C++, copy set to vector 【发布时间】:2011-06-29 09:14:44 【问题描述】:

我需要将std::set复制到std::vector

std::set <double> input;
input.insert(5);
input.insert(6);

std::vector <double> output;
std::copy(input.begin(), input.end(), output.begin()); //Error: Vector iterator not dereferencable

问题出在哪里?

【问题讨论】:

还有assign()函数:output.assign(input.begin(), input.end()); 你的向量是空的。正如人们在下面指出的那样,有很多方法可以解决这个问题。 @Gene: assign() 想要提前保留() 必要的存储量。它将使用输入迭代器来确定需要多少,除非迭代器是严格的 InputIterator,在这种情况下,它将跳过保留并导致在每个 push_back() 上重新分配。在频谱的另一端,BiderectionalIterators 将允许它只减去 end - begin。然而,std::set 的迭代器都不是(它们是 ForwardIterator),这很不幸:在这种情况下,assign() 只会遍历整个集合以确定其大小——在大型集合上性能不佳。 【参考方案1】:

您需要使用back_inserter:

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

std::copy 不会将元素添加到您要插入的容器中:它不能;它只有一个进入容器的迭代器。因此,如果您将输出迭代器直接传递给std::copy,则必须确保它指向的范围至少足以容纳输入范围。

std::back_inserter 创建一个输出迭代器,该迭代器在容器上为每个元素调用push_back,因此每个元素都插入到容器中。或者,您可以在 std::vector 中创建足够数量的元素来保存要复制的范围:

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

或者,您可以使用std::vector 范围构造函数:

std::vector<double> output(input.begin(), input.end()); 

【讨论】:

嗨,詹姆斯,而不是你的 std::copy 行(你答案中的第一个代码块),我不能只做output.insert(output.end(), input.begin(), input.end()); 吗? 或者只使用 cbegin 和 cend 版本:output.insert(output.cend(), input.cbegin(), input.cend()); 你觉得呢?谢谢。 我应该 output.reserve(input.size());是我自己做的,还是希望某些编译器为我做? @jimifiki,恐怕没希望了。 您的第一个向量初始化不正确。您创建一个 input,size() 空条目数组,然后在此之后附加附加内容。我想你的意思是使用std::vector&lt;double&gt; output; output.reserve(input.size()); std::copy(...);【参考方案2】:

只需使用带有迭代器的向量的构造函数:

std::set<T> s;

//...

std::vector v( s.begin(), s.end() );

假设您只想要 v 中 s 的内容,并且在将数据复制到 v 之前,v 中没有任何内容。

【讨论】:

【参考方案3】:

这是使用 vector::assign 的另一种选择:

theVector.assign(theSet.begin(), theSet.end());

【讨论】:

【参考方案4】:

你没有在你的向量对象中保留足够的空间来保存你的集合的内容。

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

【讨论】:

这不值得 -1。特别是,这允许向量只进行一次分配(因为它无法确定 O(1) 中集合迭代器的距离),并且,如果没有为向量定义在构造时将每个元素归零,这可能值得让副本归结为 memcpy。如果实现发现vector的ctor中的循环可以被删除,后者仍然是值得的。当然,前者也可以通过储备来实现。 我不知道。让我来帮你。 我给了你一个-1,但这是我的想法。做一个小的编辑,这样我就可以撤消我的投票,我会给你一个 +1:这实际上是一个非常干净的解决方案,因为失败优先属性。 我只是发现如果我自己编辑答案,我可以投赞成票。这样做,给了你一个+1的失败优先内存分配。对不起!【参考方案5】:

我认为最有效的方法是预先分配元素,然后放置元素:

template <typename T>
std::vector<T> VectorFromSet(const std::set<T>& from)

    std::vector<T> to;
    to.reserve(from.size());

    for (auto const& value : from)
        to.emplace_back(value);

    return to;

这样我们只会为每个元素调用复制构造函数,而不是先调用默认构造函数,然后再为上面列出的其他解决方案复制赋值运算符。更多说明如下。

    back_inserter 可以使用,但它会在向量上调用 push_back() (https://en.cppreference.com/w/cpp/iterator/back_insert_iterator)。 emplace_back() 更高效,因为它避免了在使用 push_back() 时创建临时对象。小事不成问题 构造类型,但将对性能产生影响 非平凡构造的类型(例如 std::string)。

    我们需要避免使用 size 参数构造向量 导致所有元素默认构造(一无所获)。喜欢 例如,使用 std::copy() 的解决方案。

    最后,vector::assign() 方法或采用迭代器范围的构造函数都不是好的选择 因为他们会调用 std::distance() (知道元素的数量) 在 set 迭代器上。这将导致不需要的额外迭代 通过所有 set 元素,因为该集合是二叉搜索树 数据结构,它没有实现随机访问迭代器。

希望对您有所帮助。

【讨论】:

请添加对权威的引用,为什么这很快,以及为什么不需要使用back_inserter 在答案中添加了更多说明。【参考方案6】:

std::copy 不能用于插入空容器。为此,您需要像这样使用 insert_iterator:

std::set<double> input;
input.insert(5);
input.insert(6);

std::vector<double> output;
std::copy(input.begin(), input.end(), inserter(output, output.begin())); 

【讨论】:

第一次重新分配向量时失败:来自 output.begin() 的迭代器失效。【参考方案7】:
set<T> s;
// some code
vector<T> v;
v.assign(s.begin(), s.end());

【讨论】:

【参考方案8】:

COPY 函数将一个迭代器返回到目标范围的末尾(指向复制的最后一个元素之后的元素)。

反向插入迭代器是一种特殊类型的输出迭代器,旨在允许通常覆盖元素(例如复制)的算法在容器末尾自动插入新元素。

设置操作系统; 向量vec;

复制(os.begin(), os.end(), back_inserter(vec));

【讨论】:

以上是关于C++,复制集到向量的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中将两个向量复制到另一个向量

为啥简单的 C++ 向量函数会产生越界的下标? [复制]

如何在 C++ 中将一个对象从一个向量复制到另一个向量?

C++ 将对象向量中的元素复制到具有此元素的向量中

C++:指向向量(及其元素)的指针是不是稳定,因为随着向量的增长,向量将在内存中迁移以适应更多元素? [复制]

如何使用 OpenCV 将向量的结构复制到 C++ 中的另一个向量