C++ 从STL列表中删除项目
我想做一个函数,如果项目符合某个条件,它可以将项目从一个STL列表移动到另一个STL列表 此代码不是执行此操作的方法。迭代器很可能会被erase()函数失效并导致问题:C++ 从STL列表中删除项目,c++,stl,C++,Stl,我想做一个函数,如果项目符合某个条件,它可以将项目从一个STL列表移动到另一个STL列表 此代码不是执行此操作的方法。迭代器很可能会被erase()函数失效并导致问题: for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end(); it++) { if(myCondition(*it)) { myOtherList.push_back(*it); myList.erase(i
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end(); it++)
{
if(myCondition(*it))
{
myOtherList.push_back(*it);
myList.erase(it);
}
}
for(std::list::iterator it=myList.begin();it!=myList.end();it++)
{
if(myCondition(*it))
{
myOtherList.向后推(*it);
删除(它);
}
}
那么有人能提出更好的方法吗?另一种尝试:
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end; ) {
std::list<MyClass>::iterator eraseiter = it;
++it;
if(myCondition(*eraseiter)) {
myOtherList.push_back(*eraseiter);
myList.erase(eraseiter);
}
}
for(std::list::iterator it=myList.begin();it!=myList.end;){
std::list::iterator eraseiter=it;
++它;
if(myCondition(*橡皮擦)){
myOtherList.向后推(*橡皮擦);
我的列表。擦除(橡皮擦);
}
}
擦除
指向已擦除元素后的元素:
std::list<MyClass>::iterator it = myList.begin();
while (it != myList.end())
{
if(myCondition(*it))
{
myOtherList.push_back(*it);
it = myList.erase(it);
}
else
{
++it;
}
}
std::list::iterator it=myList.begin();
while(it!=myList.end())
{
if(myCondition(*it))
{
myOtherList.向后推(*it);
它=我的列表。删除(它);
}
其他的
{
++它;
}
}
解决方案1
第二种方法使用STL实现算法。我个人认为它比第一个解决方案更具可读性,但它有两个缺点:首先,它需要输入容器使用更强大的双向迭代器,而不是我们在第一个解决方案中使用的前向迭代器。其次,这可能是您的问题,也可能不是您的问题,容器的顺序不能保证与调用std::partition()
之前相同。如果希望保持顺序,请将该调用替换为对std::stable_partition()
的调用std::stable_partition()
可能稍微慢一点,但它的运行时复杂性与std::partition()
相同
任意一种方法:调用函数
list::迭代器p=move_if(l1.begin(),l1.end(),
背面插入器(l2),
bind2nd(less(),3));
最后发言
在编写代码时,我遇到了一个难题:move\u if()
算法应该返回什么?一方面,算法应该返回一个迭代器,指向输入容器的新结束位置,这样调用者就可以使用erase-remove习惯用法来收缩容器。但另一方面,算法应该返回结果容器末尾的位置,因为否则调用方查找结果容器的代价可能会很高。在第一种解决方案中,当算法结束时,结果
迭代器指向此位置,而在第二种解决方案中,std::copy()
返回的迭代器指向此位置。我可以返回一对迭代器,但为了简化操作,我只返回其中一个迭代器。template
template <typename ForwardIterator, typename OutputIterator, typename Predicate>
void splice_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, Predicate pred)
{
ForwardIterator it = begin;
while( it != end )
{
if( pred(*it) )
{
*begin++ = *out++ = *it;
}
++it;
}
return begin;
}
myList.erase(
splice_if( myList.begin(), myList.end(), back_inserter(myOutputList),
myCondition
),
myList.end()
)
如果(ForwardIterator begin、ForwardIterator end、OutputIterator out、谓词pred)无效
{
ForwardIterator it=begin;
while(it!=结束)
{
if(pred(*it))
{
*开始+++=*输出+++=*它;
}
++它;
}
返回开始;
}
删除(
拼接if(myList.begin()、myList.end()、back_插入器(myOutputList),
霉菌病
),
myList.end()
)
STL列表有一个有趣的功能:使用splice()
方法可以将元素从一个列表破坏性地移动到另一个列表
splice()
以恒定时间运行,不复制元素或执行任何免费存储分配/解除分配。请注意,两个列表必须是相同的类型,并且它们必须是单独的列表实例(而不是对同一列表的两个引用)
下面是一个如何使用splice()
的示例:
for(std::list::iterator it=myList.begin();it!=myList.end();){
if(myCondition(*it)){
std::list::迭代器oldIt=it++;
拼接(myOtherList.end(),myList,oldIt);
}否则{
++它;
}
}
std::list::迭代器endMatching=
分区(myList.begin()、myList.end()、myCondition);
拼接(myOtherList.begin(),myList,endMatching,myList.end());
请注意,partition()为您提供了足够的空间来区分匹配对象和非匹配对象。
(然而,列表::splice()很便宜)
请参见以下代码,该代码来自
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
CPred类:公共一元函数
{
公众:
CPred(常量字符串和arString)
:mString(arString)
{
}
布尔运算符()(常量字符串和arString)常量
{
返回(arString.find(mString)=std::string::npos);
}
私人:
字符串mString;
};
int main()
{
列出字符串;
字符串。推回(“213”);
字符串。推回(“145”);
字符串。推回(“ABC”);
字符串。推回(“167”);
字符串。推回(“DEF”);
你的意思是说remove_if?另外,myList.erase会引起问题,不是吗?否则,这就是我要发布的内容。=]我通常使用erase(it++),而不是依赖于erase的返回值,因为其他STL容器并不总是返回更新的迭代器。@haggai_e:标准要求erase()方法返回相关的迭代器。不遵循此方法的实现不符合标准,应避免使用。@haggai_e:每个STL容器都返回一个更新的迭代器。另一方面,您的++技巧不能保证适用于所有迭代器。(例如,在向量中,它将无效,因此您将无法增加它)@wilhemltell,@jalf:not true.std::map和其他关联容器类从erase函数返回void。但是,任何序列都会从erase返回以下迭代器。不起作用。首先,如果您决定使用迭代器,则无法收缩列表。其次,您无法访问算法内部的myList。
template<typename Bidi, typename Out, typename Operation>
Bidi move_if(Bidi first, Bidi last, Out result, Operation op)
{
Bidi new_end = partition(first, last, not1(op));
copy(new_end, last, result);
return new_end;
}
list<int>::iterator p = move_if(l1.begin(), l1.end(),
back_inserter(l2),
bind2nd(less<int>(), 3));
template <typename ForwardIterator, typename OutputIterator, typename Predicate>
void splice_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, Predicate pred)
{
ForwardIterator it = begin;
while( it != end )
{
if( pred(*it) )
{
*begin++ = *out++ = *it;
}
++it;
}
return begin;
}
myList.erase(
splice_if( myList.begin(), myList.end(), back_inserter(myOutputList),
myCondition
),
myList.end()
)
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end(); ) {
if(myCondition(*it)) {
std::list<MyClass>::iterator oldIt = it++;
myOtherList.splice(myOtherList.end(), myList, oldIt);
} else {
++it;
}
}
std::list<MyClass>::iterator endMatching =
partition(myList.begin(), myList.end(), myCondition);
myOtherList.splice(myOtherList.begin(), myList, endMatching, myList.end());
#include <iostream>
#include <iterator>
#include <list>
#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()
{
list<string> Strings;
Strings.push_back("213");
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");
// Linear. Exactly last - first applications of pred, and at most (last - first)/2 swaps.
list<string>::iterator end1 =
partition(Strings.begin(), Strings.end(), Pred);
list<string> NotMatching;
// This function is constant time.
NotMatching.splice(NotMatching.begin(),Strings, Strings.begin(), end1);
cout << "Elements matching with 1" << endl;
copy(Strings.begin(), Strings.end(), ostream_iterator<string>(cout,"\n"));
cout << "Elements not matching with 1" << endl;
copy(NotMatching.begin(), NotMatching.end(), ostream_iterator<string>(cout,"\n"));
return 0;
}