3

逆イテレータを使用して std::list をトラバースし、挿入時に取得された順方向イテレータを使用してリストからいくつかの要素を消去しています。以下にサンプルプログラムを示します。リストから要素を削除しても、削除された要素を参照するものを除いて、他のイテレータは無効にならないことを読みました。しかし、reverse_iterators について言及されておらず、プログラムがクラッシュしています。使用法が間違っているかどうか誰か教えてください。

プログラムが行っていることは、要素をリストに追加し、その反復子を格納し、リストを逆に反復し、格納された反復子を使用してリスト内の唯一の要素を削除することです。

出力は、コード サンプルの下に貼り付けられます。

#include <list>
#include <iostream>
using namespace std;

struct node
{
    int data;
    list<node*>::iterator iter;
} a;

int main()
{
    list<node*> l;
    a.data = 1;
    l.push_front( &a );
    a.iter = l.begin();
    list<node*>::reverse_iterator ri = l.rbegin();
    while ( ri != l.rend() )
    {
        cout << (*ri)->data << endl;
        list<node*>::reverse_iterator rj = ri;
        ++ri;
        if ( ri ==  l.rend() )
            cout << "before erase: reached end" << endl;
        l.erase((*rj)->iter);
        if ( ri ==  l.rend() )
            cout << "after erase : reached end" << endl;
        else
            cout << "after erase : Not reached end" << endl;
    }
}

出力

1
before erase: reached end
after erase : Not reached end
610568524
before erase : reached end
Segmentation fault
4

4 に答える 4

2

VS2010では、最初のループパスで例外がスローされます。

 l.erase((*rj)->iter);
 if ( ri ==  l.rend() ) // exception

それはあなたに何が起こっているのかについての一般的な考えを与えるはずです。ご覧reverse_iteratorのとおり、これは標準のイテレータの単なるラッパーです。とは言うものの、基礎となるイテレータを返すメンバーがあることを覚えておく必要がbase()あります。structのように、他の場所に格納する必要はありませんnode

これは、にどのようにreverse_iterator関連するかについてのすばらしい答えiteratorです。あなたの場合、イテレータrbeginに基づきます。beginリストからを削除するとbegin(要素が1つしかないため、削除します)、reverse_iteratorこれに基づくすべてのが無効iteratorになります。そのことを念頭に置いて、ループを次のように書き直すことができます。

while ( ri != l.rend() )
{  
    cout << (*ri)->data << endl;
    list<node*>::reverse_iterator rj = ri;
    ++ri;

    if ( ri ==  l.rend() )
        cout << "before erase: reached end" << endl;

    // the actual underlying iterator has an offset of one
    list<node*>::iterator it = l.erase(--rj.base());
    ri = reverse_iterator<list<node*>::iterator>(it);
    // or just
    // ri = reverse_iterator<list<node*>::iterator>(l.erase(--rj.base()));

    if ( ri ==  l.rend() )
        cout << "after erase : reached end" << endl;
    else
        cout << "after erase : Not reached end" << endl;
}
于 2012-07-05T07:57:14.320 に答える
1

逆反復子は (典型的には) 一意のクラスではありませんが、通常の反復子のアダプターです。メンバーとしてリストへの反復子があり、それを使用して独自の移動と逆参照を行います。したがって、このリスト反復子が無効になると、逆反復子も無効になります。

于 2012-07-05T07:27:00.270 に答える
0

調査結果をメモするために回答を書いています。この問題が発生した場合は、試してください

SingerOfTheFall の提案された方法、それは魅力のように機能します。例:

        for(auto it=values.end();it!=values.begin();){
            if((*it).second.endPoint)
                break;
            values.erase((*(it--)).first);
        }

これに関する私の調査結果に戻ります。

プログラムがハングした問題に遭遇し、valgrindチェックを実行したところ、いくつかの奇妙なものがドロップアウトしましInvalid readslibstdc++

Invalid read of size 8
    at 0x4EAA633: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20)
    by 0x402FEC: std::_Rb_tree_iterator<std::pair<int const, GroupControl::Group::Entry> >::operator--() (stl_tree.h:218)

最後の要素が消去された後、rend()イテレータが停止せず、++op がループに閉じ込められていると思われます

于 2015-01-28T23:02:45.880 に答える
-1

消去の戻り値をイテレータに格納する必要があります。以下の変更を行います。

(*rj)->iter= l.erase((*rj)->iter);
于 2012-07-05T07:40:17.233 に答える