如何从stl数据结构中删除reverse_iterator?
题
由于某种原因,以下代码失败。您不能简单地使用其base()方法擦除reverse_iterator。
#include <set>
#include <iostream>
int main()
{
std::set<int> setOfInts;
setOfInts.insert(1);
setOfInts.insert(2);
setOfInts.insert(3);
std::set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
std::set<int>::reverse_iterator nextRevIter = setOfInts.rbegin();
++nextIter;
while ( rev_iter != setOfInts.rend())
{
// Find 3 and try to erase
if (*rev_iter == 3)
{
// SEGFAULT HERE
setOfInts.erase( rev_iter.base());
}
rev_iter = nextRevIter;
++nextRevIter;
}
}
如何正确地完成上述操作?如果reverse_iterator对应于您要擦除的内容,那么如何删除它?
注意,不幸的是,擦除不会使用reverse_iterator。它想要真实的东西。
解决方案
显然,解决方案是base()返回的是1。对于reverse_iterator,以下标识成立:
&*(reverse_iterator(i)) == &*(i - 1)
或者换句话说,reverse_iterator始终是一个通过它的基础的常规迭代器。不知道为什么。
在GCC
只需更改
// SEGFAULT HERE
setOfInts.erase( rev_iter.base());
到
// WORKS!
setOfInts.erase( --rev_iter.base());
我很清楚,为什么上面的身份才有意义。
在Visual Studio中
回到工作中并在visual studio中尝试这个,我看到上面的解决方案不太有效。 “nextIter”是指“nextIter”。擦除时变为无效。相反,你需要将擦除中的临时值保存起来以获得下一个迭代器,而不是保持像上面那样的nextIter。
set<int>::iterator tempIter = setOfInts.erase(--rev_iter.base());
rev_iter = setOfInts.erase(tempIter);
所以最终的解决方案是
int main()
{
using namespace std;
set<int> setOfInts;
setOfInts.insert(1);
setOfInts.insert(2);
setOfInts.insert(3);
set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
while ( rev_iter != setOfInts.rend())
{
// Find 3 and try to erase
if (*rev_iter == 3)
{
cout << "Erasing : " << *rev_iter;
set<int>::iterator tempIter = setOfInts.erase( --rev_iter.base());
rev_iter = set<int>::reverse_iterator(tempIter);
}
else
{
++rev_iter;
}
}
}
注意,关联容器不会从erase返回迭代器。所以这个解决方案不适用于map,multimap等。
其他提示
当您使用反向迭代器进行迭代并希望使用base()来修改其容器时,请始终记住,reverse_iterator始终基于原始顺序中的下一个迭代器。这有点不直观,但它实际上使代码更简单:
#include <set>
int main()
{
std::set<int> setOfInts;
setOfInts.insert(1);
setOfInts.insert(2);
setOfInts.insert(3);
typedef std::set<int>::reverse_iterator RevIter;
RevIter rev_iter = setOfInts.rbegin();
while (rev_iter != setOfInts.rend())
{
// Find 3 and try to erase
if (*rev_iter == 3)
setOfInts.erase(--rev_iter.base());
++rev_iter;
}
}
在这个例子中,不需要保持“下一个”时间。迭代器,因为基础迭代器没有失效! (在处理普通迭代器时我们确实需要它。)
反向迭代器的行为在处理单个项目时会产生奇怪的一对一困难,但事实上它简化了范围:
riValue = find(riEnd.base(), riBegin.base(), value);
使用与
完全相同的对象(顺序相反)iValue = find(riBegin, riEnd, value);
1来自 map :: erase ,我们知道只需要 iterator
;
2来自 reverse_iterator :: base ,我们知道&amp; *(reverse_iterator(i))==&amp; *(i&#8211; 1)。
因此,您可以擦除( - r_v.base())以擦除“r_v”指向的元素。 (和“current-1”):
r_v+1 r_v r_v-1
current-2 current-1 current
使用迭代器本身调用 erase
(无需使用 base
)。
#include <set>
#include <iostream>
int main()
{
std::set<int> setOfInts;
setOfInts.insert(1);
setOfInts.insert(2);
setOfInts.insert(3);
std::set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
while (rev_iter != setOfInts.rend())
{
// Find 3 and try to erase
if (*rev_iter == 3)
{
rev_iter = setOfInts.erase(rev_iter);
}
else
{
++rev_iter;
}
}
}
此外,您不需要单独的“下一个”迭代器(见上面的变化)。更好的方法是使用 std :: remove_if
(或类似的函数)。