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测试)。
这是否真的表现出色,或者我是否正确地担心我只是在分享参考?
答案 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);
}
这至少不应该复制任何元素。它基本上将要保留的元素分离出来并留在原始向量中。然后有效地移出正在离开的那些。然后只需调整阵列大小。正如评论中指出的那样,这可能涉及与最佳操作相比的额外操作,但我的感觉是最佳版本对于编码并不简单,并且可能涉及权衡(如更多状态),因此它可能不是纯粹的胜利
请注意,我的算法专门接受一个向量而不是迭代器,因为对于其他容器(比如一个链表),这个实现远非最优。