当我在迭代std::list时,我能够从中删除元素吗?例如:
std::list<int> lst;
//....
for (std::list<int> itr = lst.begin(); itr != lst.end(); itr++)
{
if (*itr > 10)
lst.remove(*itr);
}
为什么?
And why?正确的代码如下:
for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); /*nothing*/)
{
if (*itr > 10)
itr = lst.erase(itr);
else
++itr;
}
从列表中删除一个项时,如果迭代器指向要删除的项,则可能会使迭代器失效。因此,您需要使用erase
进行删除(它返回指向下一项的有效迭代器)。
更好的方法是使用std::remove_if
:
bool greater_than_10(int x)
{
return x > 10;
}
lst.remove_if(greater_than_10);
如果您的编译器支持lambda表达式,您可以将其缩短:
lst.remove_if([](int x){ return x > 10; });
(我的编译器太旧了,无法测试此代码;lambda函数来自@John Dibling的回答。)
事实上,从列表中删除元素仅会使指向被删除项的迭代器失效,仅仅如此。但要注意,其他STL容器没有这种属性。
简而言之:通常情况下,在通过列表进行迭代时不应该删除其中的项,因为删除可能会使迭代器失效(程序可能会崩溃)。但是,如果你完全确定你要删除的项不是任何你在删除时使用的迭代器引用的值,那么你可以进行删除操作。
请注意,对于其他STL容器(例如向量),限制会更严格:从容器中删除项不仅会使指向已删除项的迭代器失效,还可能使其他迭代器失效!因此,在遍历这些容器时进行删除操作更加棘手。
remove_if
的示例代码,希望你不介意。此外,我将 itr
的类型从 std::list
更正为 std::list<int>::iterator
。 - fredoverflow不行。示例代码使 itr
无效,导致未定义的行为。但是以下代码可以正常工作:
for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); )
{
if (*itr > 10)
itr = lst.erase(itr);
else
++itr;
}
std::remove_if
和一个表明“大于10”的函数对象,就像这样:#include <list>
#include <algorithm>
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), std::bind2nd(std::greater<int>(), 10)), lst.end());
}
另一种更通用的方法是编写自己的自定义函数对象(functor)。下面是一个名为is_a_match
的函数对象,如果被检查的值大于10,则返回true
。您可以重新定义operator()
以返回true
,以对应于在您的情况下“匹配”的含义:
#include <list>
#include <algorithm>
#include <functional>
struct is_a_match : public std::unary_function<int, bool>
{
is_a_match(int val) : val_(val) {};
bool operator()(int victim) const { return victim > val_; }
private:
int val_;
};
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), is_a_match(10) ));
}
#include <list>
#include <algorithm>
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int v) {return v > 10;}));
}
list
有自己的remove_if
成员函数:lst.remove_if(std::bind2nd(std::greater<int>(), 10));
- Fred Larson我认为你可以这样做,但是在删除元素后,你必须重新分配迭代器,这可以通过使用erase
方法而不是remove
来完成。
否则,这将是不安全的,不应该这样做。
请查看 http://www.cppreference.com/wiki/iterator/start 获取迭代器的描述。
几个注意事项:
++itr
)而不是后缀递增运算符(itr++
)