使用擦除 - 删除范例将元素从一个向量移动到另一个向量

时间:2017-03-03 21:48:18

标签: c++ c++11 vector move

This question清楚地阐明了如何将内容从一个std::vector移动到另一个std::move。简而言之,实际移动内存需要进行std::erase调用,同时还需要// Vector with values [0, 1, ..., 19] std::vector<int> myOldVec; for (int i = 0; i < 20; i++) { myOldVec.push_back(i); } // New vector to move into std::vector<int> myNewVec; // Move from myOldVec to myNewVec if value is less than 5 myOldVec.erase( std::remove_if( myOldVec.begin(), myOldVec.end(), [&](const int x) { if (x < 5) { myNewVec.push_back(x); return true; } return false; } ), myOldVec.end() ); 调用来调整原始向量的大小以解释已删除的元素。

使用erase-remove paradigm执行此操作是否存在问题,因为人们在迭代它时使用它来删除它(如here)?

例如,像这样:

myOldVec --> [5, 6, ..., 19]
myNewVec --> [0, 1, 2, 3, 4]

预期输出为

int

当我运行此代码时,它可以在我的测试仪中运行。但是,在处理对象而不是std::vector<MyObj>时,我担心我实际上并没有移动任何东西,而只是引用;例如,当使用{{1}}进行上述操作时(使用适当的lambda测试)。

这是否真的表现出色,或者我是否正确地担心我只是在分享参考?

1 个答案:

答案 0 :(得分:4)

我认为通常这些算法的重点在于,您正在通过应用函数来实现您想要实现的目标。一旦这些函数产生副作用,似乎任何结果都可能产生误导,并且做一个for循环可能更好。

那就是说,记住C ++不是Java。 vector<Foo>必须存储Foo,它不能存储引用。但是,你的整个想法仍然存在问题。

myNewVec.push_back(x);

代码中的这一行会将x副本推送到新的向量中。因为它是副本,所以您不必担心共享引用。现在,对于整数,副本和移动是相同的。但对于复杂的对象(例如,矢量),移动可能比复制更快。而唯一的矢量无论如何摆脱x,所以我们肯定想要移动。理想情况下,我们会将该行更改为:

myNewVec.push_back(std::move(x));

但是,从一个物体移动会明显改变它,并要求它不是常量。但是remove_if的要求要求传递的函数对象是谓词。这反过来意味着:

  

函数对象pred不应通过解除引用的迭代器应用任何非常量函数。

http://en.cppreference.com/w/cpp/concept/Predicate

换句话说,你的函数必须接受取消引用迭代器的结果,但不应该改变它。因为它不应该改变它,你永远不能从原始对象移动,但必须复制它。因此,我认为对于非平凡类型,这个想法没有任何符合要求,有效的实现。

这是一个合理的实施:

template <class T, class F>
void transfer_if_not(std::vector<T>& old, std::vector<T>& new, F pred)
{
    auto part = std::partition(old.begin(), old.end(), pred);
    std::move(part, old.end(), std::back_inserter(new));
    old.erase(part);
}

这至少不应该复制任何元素。它基本上将要保留的元素分离出来并留在原始向量中。然后有效地移出正在离开的那些。然后只需调整阵列大小。正如评论中指出的那样,这可能涉及与最佳操作相比的额外操作,但我的感觉是最佳版本对于编码并不简单,并且可能涉及权衡(如更多状态),因此它可能不是纯粹的胜利

请注意,我的算法专门接受一个向量而不是迭代器,因为对于其他容器(比如一个链表),这个实现远非最优。

相关问题