我有一个字符串的源容器我想从源容器中删除与谓词匹配的任何字符串,并将它们添加到目标容器中.
remove_copy_if和其他算法只能重新排序容器中的元素,因此必须由erase成员函数跟进.我的书(Josuttis)说remove_copy_if在目标容器中的最后一个位置之后返回一个迭代器.因此,如果我只在目标容器中有一个迭代器,我erase该如何调用源容器?我已经尝试使用目标的大小来确定从源容器的末尾回去多远,但没有运气.我只提出了以下代码,但它会进行两次调用(remove_if和remove_copy_if).
有人能让我知道正确的方法吗?我确信两次线性调用不是这样做的方法.
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
vector<string> Container;
Strings.push_back("123");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
remove_copy_if(Strings.begin(), Strings.end(),
back_inserter(Container),
Pred);
Strings.erase(remove_if(Strings.begin(), Strings.end(),
not1(Pred)), Strings.end());
cout << "Elements beginning with 1 removed" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
cout << "Elements beginning with 1" << endl;
copy(Container.begin(), Container.end(),ostream_iterator<string>(cout,"\n"));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
尽管弗雷德的辛勤工作得到应有的尊重,但我还是要补充一点:这与抽象层面move_if没有什么不同remove_copy_if.唯一的实现级别更改是end()迭代器.你还没有得到任何erase().接受的答案不是erase()匹配的元素 - OP的问题陈述的一部分.
至于OP的问题:你想要的是就地拼接.这可用于列表.但是,vectors这不会起作用.了解迭代器何时以及如何以及为何失效.您将不得不采用两遍算法.
remove_copy_if和其他算法只能重新排序容器中的元素,
来自SGI的文档remove_copy_if:
此操作是稳定的,这意味着复制的元素的相对顺序与[first,last]范围内的相对顺序相同.
因此,不会发生相对重新排序.而且,这是一个副本,这意味着Source vector你的情况下的元素被复制到了Container vector.
如何在源容器上调用erase?
您需要使用不同的算法,称为remove_if:
remove_if从范围中移除[first, last)的每个元素x,使得pred(x)为真.也就是说,remove_if返回一个迭代器new_last,使得该范围不[first, new_last)包含任何pred为true的元素.范围内的迭代器[new_last, last)仍然可以解除引用,但它们指向的元素未指定.Remove_if是稳定的,这意味着未被移除的元素的相对顺序不变.
所以,只需将该remove_copy_if调用更改为:
vector<string>::iterator new_last = remove_if(Strings.begin(),
Strings.end(),
Pred);
Run Code Online (Sandbox Code Playgroud)
你们都准备好了.请记住,你Strings vector的范围不再是由迭代器定义的,[first(), end())而是由[first(), new_last).
如果您愿意,可以[new_last, end())通过以下方式删除剩余部分:
Strings.erase(new_last, Strings.end());
Run Code Online (Sandbox Code Playgroud)
现在,你vector已经被缩短,您end()和new_last是相同的(一个过去的最后一个元素),所以你可以一如既往地使用:
copy(Strings.begin(), Strings.end(), ostream_iterator(cout, "\"));
Run Code Online (Sandbox Code Playgroud)
在控制台上打印字符串(stdout).
我明白你的观点,你要避免对源容器进行两次传递.不幸的是,我不相信有一个标准算法可以做到这一点.可以创建自己的算法,将元素复制到新容器并从源容器中删除(与remove_if相同;之后必须进行擦除).您的容器大小和性能要求将决定创建此类算法的努力是否优于进行两次传递.
编辑:我想出了一个快速实现:
template<typename F_ITER, typename O_ITER, typename FTOR>
F_ITER move_if(F_ITER begin, F_ITER end, O_ITER dest, FTOR match)
{
F_ITER result = begin;
for(; begin != end; ++begin)
{
if (match(*begin))
{
*dest++ = *begin;
}
else
{
*result++ = *begin;
}
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
编辑:也许"通行证"意味着混乱.在OP的解决方案中,调用remove_copy_if()并调用remove_if().每个都将遍历整个原始容器.然后调用erase().这将遍历从原始容器中删除的任何元素.
如果我的算法用于将删除的元素复制到新容器(使用begin(),输出迭代器的原始容器将无法工作,如Dirkgently演示),它将执行一次传递,将删除的元素复制到新容器中back_inserter的手段或某种这样的机制.与remove_if()一样,仍然需要擦除.消除了原始容器的一次通过,我认为这是OP所追求的.