我需要一种方法来从范围中删除满足特定条件的所有元素(在此特定情况下为std::vector
)并将这些已删除的元素复制到新范围(所以类似std::remove_if
输出参数。)此操作后,输入范围的顺序和输出范围的顺序都不相关。
一种天真的方法是使用std::partition
来查找所有“邪恶”元素,然后复制它们并最后删除它们,但这会在没有必要的情况下两次触及所有“邪恶”元素。
或者我可以自己编写所需的remove_if
变体,但为什么要重新发明轮子(另外我不知道我是否可以匹配高质量库实现的效率)。
所以问题是:
这样的功能是否已存在? 允许Boost,但首选标准C ++(该项目还不依赖于boost)。
如果没有,是否有一种智能算法比一个天真的手工remove_if
变体更快?
答案 0 :(得分:2)
不,它没有。有一个函数可以执行一个(删除与谓词匹配的元素)或另一个函数(复制匹配谓词的元素),但不能同时执行两个。但是,通过两个步骤编写我们自己的内容非常容易:
template <typename InputIter, typename OutputIter, typename UnaryPredicate>
InputIter remove_and_copy(InputIter first, InputIter last,
OutputIter d_first, UnaryPredicate pred)
{
std::copy_if(first, last, d_first, pred);
return std::remove_if(first, last, pred);
}
用作:
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7};
std::vector<int> u;
v.erase(
remove_and_copy(v.begin(), v.end(), std::back_inserter(u),
[](int i) { return i%2 == 0; }),
v.end()
);
// now v is {1, 3, 5, 7} and u is {2, 4, 6}
如果你只需要vector
,你可以稍微改写一下,但仍然只有两行:
template <typename T, typename UnaryPredicate>
void remove_and_copy(std::vector<T>& from, std::vector<T>& to, UnaryPredicate pred)
{
std::copy_if(from.begin(), from.end(), std::back_inserter(to), pred);
from.erase(std::remove_if(from.begin(), from.end(), pred), from.end());
}
或者编写自己的循环:
template <typename T, typename UnaryPredicate>
void remove_and_copy(std::vector<T>& from, std::vector<T>& to, UnaryPredicate pred)
{
for (auto it = from.begin(); it != from.end(); ) {
if (pred(*it)) {
to.push_back(*it);
it = from.erase(it);
}
else {
++it;
}
}
}
使用:
remove_and_copy(v, u, [](int i) { return i%2 == 0; });
答案 1 :(得分:2)
使用迭代器时删除的问题是您无法访问实际容器,因此您无法实际删除这些元素。相反,例如,std::remove()
所做的是将目标范围移动到范围的末尾,容器稍后将使用该范围来实际删除元素。
相反,您可以让您的函数将流作为参数,以便在找到目标值后调用其删除方法:
#include <algorithm>
#include <iterator>
#include <string>
#include <iostream>
template <typename Container, typename OutputIt, typename UnaryPredicate>
auto remove_and_copy_if(Container& c, OutputIt d_first, UnaryPredicate pred)
-> decltype(c.begin())
{
auto it = std::begin(c);
for (; it != std::end(c); )
{
while (it != std::end(c) && pred(*it))
{
d_first++ = *it;
it = c.erase(it);
}
if (it != std::end(c)) ++it;
}
return it;
}
template <typename Container, typename OutputIt, typename T>
auto remove_and_copy(Container& c, OutputIt d_first, T const& value)
-> decltype(c.begin())
{
return remove_and_copy_if(c, d_first,
[&] (T const& t) { return t == value; });
}
int main()
{
std::string str = "Text with some spaces ";
std::string output;
std::cout << "Before: " << str << '\n';
remove_and_copy(str, std::back_inserter(output), ' ');
std::cout << "After: " << str << '\n';
std::cout << "Characters removed: " << output << '\n';
}