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_iteratorsは使用されません。本物が欲しい。
解決
どうやら、解決策はbase()が1を返すことです。 reverse_iteratorには次のIDが保持されます。
&*(reverse_iterator(i)) == &*(i - 1)
つまり、reverse_iteratorは、ベースとなる通常のイテレーターを常に1回渡します。理由はわかりません。
GCCで
簡単に変更
// SEGFAULT HERE
setOfInts.erase( rev_iter.base());
to
// WORKS!
setOfInts.erase( --rev_iter.base());
なぜ上記のアイデンティティが理にかなっているのかについては間違いなく興味があります。
Visual Studioで
仕事に戻って、Visual Studioでこれを試してみると、上記の解決策はまったく機能しないことがわかります。 「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;
}
}
}
注意、連想コンテナは消去からイテレータを返しません。そのため、このソリューションはマップ、マルチマップなどでは機能しません。
他のヒント
逆イテレータで反復し、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 からa>、それは iterator
;
2 reverse_iterator :: base からa>、&amp; *(reverse_iterator(i))==&amp; *(i&#8211; 1)。
したがって、erase(-r_v.base())で&quot; r_v&quot;が指す要素を消去できます。 (および&quot; current-1&quot;):
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
(またはそのような関数)を使用することです。