15

何らかの理由で、次のコードは失敗します。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 が与えられた場合、どのように消去しますか?

残念ながら、erase は reverse_iterators を取りません。それは本物が欲しい。

4

4 に答える 4

18

どうやら解決策は、 base() が返すものは1オフです。次の同一性は、reverse_iterator に対して保持されます。

&*(reverse_iterator(i)) == &*(i - 1) 

言い換えれば、reverse_iterator は常に、それがベースである通常の反復子を 1 回通過します。理由がわからない。

GCC で

変更するだけ

        // SEGFAULT HERE
        setOfInts.erase( rev_iter.base());

        // WORKS!
        setOfInts.erase( --rev_iter.base());

上記の同一性がなぜ理にかなっているのかについては、私は間違いなく興味があります。

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;
        }
    }   

}

連想コンテナは消去からイテレータを返さないことに注意してください。したがって、このソリューションはマップ、マルチマップなどでは機能しません。

于 2008-12-31T23:25:32.003 に答える
3

逆反復子を使用して反復し、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);
于 2009-05-04T16:57:45.210 に答える
-3

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(またはそれに似た関数)を使用することです。

于 2008-12-31T23:22:57.767 に答える