リバースイテレータで消去を呼び出す方法


181

私はこのようなことをやろうとしています:

for ( std::list< Cursor::Enum >::reverse_iterator i = m_CursorStack.rbegin(); i != m_CursorStack.rend(); ++i )
{
    if ( *i == pCursor )
    {
        m_CursorStack.erase( i );
        break;
    }
}

ただし、消去には反復子が必要であり、逆反復子は必要ありません。逆反復子を通常の反復子に変換する方法、またはこの要素をリストから削除する別の方法はありますか?


17
余談ですが、このようなループを作成するときは、ここで行うように、終了イテレータを繰り返し計算しないでくださいi != m_CursorStack.rend()。代わりに、と書きi = m_CursorStack.rbegin(), end = m_CursorStack.rend(); i != end;ます。つまり、繰り返し比較するために保持できるイテレータを初期化します-ループ本体の副作用として終了位置が変化しないことを前提とします。
seh

ここでの明らかな質問は、なぜこれを行うのかということです。リストを逆にたどることで何が得られますか?このコードを使用する代わりに自分で書くことで何が得られstd::removeますか?
Jerry Coffin

std :: listのイテレータは、それが参照する要素が消去された後でもインクリメントできますか?
スティーブジェソップ

3
私は1つの要素だけを削除したいので、 'break;'を使用すると、 'remove'を使用すると、一致に時間がかかり、必要な処理を実行できなくなります。この特定のケースで削除したい要素は、ほとんど常にリストの最後またはそれに非常に近いため、逆方向に反復することもより速く、問題により適しています。
0xC0DEFACE 2009

4
stackoverflow.com/a/2160581/12386これは、ユーザーとしてのあなたが知っているか気にする必要がないので、デザイナーが実装を明確に定義していないことを示していますそして高価。
stu '14年

回答:


181

さらに調査とテストを行った結果、解決策が見つかりました。どうやら標準[24.4.1 / 1]によると、i.base()とiの関係は次のとおりです。

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

ドブス博士の記事より):

代替テキスト

したがって、base()を取得するときにオフセットを適用する必要があります。したがって、解決策は次のとおりです。

m_CursorStack.erase( --(i.base()) );

編集

C ++ 11用に更新しています。

reverse_iterator iは変更されていません。

m_CursorStack.erase( std::next(i).base() );

reverse_iterator iは高度です:

std::advance(i, 1);
m_CursorStack.erase( i.base() );

これは私の以前の解決策よりもはるかに明確だと思います。必要に応じて使用してください。


27
引用した記事をもう少しメモする必要があります。移植性を高めるためには、式を記述しますm_CursorStack.erase( (++i).base())(逆に、反復子を使用してこれを行うと頭が痛くなります...)。DDJの記事がMeyerの「Effective STL」の本に組み込まれていることにも注意してください。
マイケル・バー、

8
この図は参考になるよりもわかりにくいと思います。rbegin、ri、およびrendはすべて、実際には、描かれているものの右にある要素を指しています。この図は、あなたが*それらの要素にアクセスした場合にどの要素にアクセスするかを示していますがbase、ここでは、それらの要素の場合、どの要素を指しているかについて説明します。イテレータを変更するので、私は--(i.base())(++i).base()ソリューションのファンではありません。私は(i+1).base()どちらもうまくいくことを好みます。
mgiuca

4
逆イテレータは嘘つきです。逆の場合、逆イテレータはそのの要素を返します。こちらをご覧ください
ボボボボ2013

4
ただ明確にするために、この手法は通常のforループ(反復子が通常の方法でインクリメントされる)ではまだ使用できません。stackoverflow.com/questions/37005449/…を
logidelic

1
m_CursorStack.erase((++ i).base())は、++ iで最後の要素を過ぎてしまうと問題になるようです。end()でeraseを呼び出すことができますか?
STU

16

ループ(元の質問を参照)で使用するとiの値が変わるためm_CursorStack.erase( (++i).base())、問題になる可能性があることに注意してくださいfor。正しい表現はm_CursorStack.erase((i+1).base())


4
はイテレータで機能しないiterator j = i ; ++jため、イテレータのコピーを作成して作成する必要がありi+1ますが、それは正しい考えです
bobobobo

3
@ bobobobo、m_CursorStack.erase(boost::next(i).base())Boostで使えます。またはC ++ 11でm_CursorStack.erase(std::next(i).base())
alfC '10年

12

...またはこの要素をリストから削除する別の方法?

これには-std=c++11フラグが必要です(の場合auto):

auto it=vt.end();
while (it>vt.begin())
{
    it--;
    if (*it == pCursor) //{ delete *it;
        it = vt.erase(it); //}
}

魅力的な作品です:)
Den-Jason

@GaetanoMendola:なぜ?
slashmais

3
リストの反復子が順序付けされていることを誰が保証しますか?
ガエタノメンドーラ2018年

7

このページにはまだ正しい解決策がないのはおかしい。したがって、以下は正しいものです。

順方向反復子の場合、解決策は簡単です。

std::list< int >::iterator i = myList.begin();
while ( ; i != myList.end(); ) {
  if ( *i == to_delete ) {
    i = myList.erase( i );
  } else {
    ++i;
  } 
}

逆反復子の場合も同じようにする必要があります:

std::list< int >::reverse_iterator i = myList.rbegin();
while ( ; i != myList.rend(); ) {
  if ( *i == to_delete ) {
    i = decltype(i)(myList.erase( std::next(i).base() ));
  } else {
    ++i;
  } 
}

ノート:

  • あなたは reverse_iteratorイテレータからます
  • の戻り値を使用できます std::list::erase

このコードは機能しますが、nextが使用される理由と、世界を崩すことなく順方向反復子を逆方向反復子にキャストすることが安全な方法を説明してください
Lefteris E

1
@LefterisEそれはキャストではありません。これは、interatorから新しい逆反復子を作成します。これは、逆反復子の通常のコンストラクタです。
サイモン・トス

3

ここでreverse_iteratorbase()メソッドを使用して結果をデクリメントすることreverse_iteratorはできますが、には通常iteratorのと同じステータスが与えられないことに注意してください。一般に、このような理由から、sよりも(sおよびsよりも)通常iteratorのsを優先する必要があります。理由の詳細については、Doctor Dobbs 'Journalを参照してください。reverse_iteratorconst_iteratorconst_reverse_iterator


3
typedef std::map<size_t, some_class*> TMap;
TMap Map;
.......

for( TMap::const_reverse_iterator It = Map.rbegin(), end = Map.rend(); It != end; It++ )
{
    TMap::const_iterator Obsolete = It.base();   // conversion into const_iterator
    It++;
    Map.erase( Obsolete );
    It--;
}

3

そして、逆に反復しながらコンテナ内の要素を消去するために、消去の結果を逆反復子に変換するコードの一部を次に示します。少し奇妙ですが、最初または最後の要素を消去する場合でも機能します。

std::set<int> set{1,2,3,4,5};

for (auto itr = set.rbegin(); itr != set.rend(); )
{    
    if (*itr == 3)
    {
        auto it = set.erase(--itr.base());
        itr = std::reverse_iterator(it);            
    }
    else
        ++itr;
}

2

途中ですべてを消去する必要がない場合は、問題を解決するために、erase-removeイディオムを使用できます。

m_CursorStack.erase(std::remove(m_CursorStack.begin(), m_CursorStack.end(), pCursor), m_CursorStack.end());

std::removepCursor最後に一致するコンテナ内のすべてのアイテムを交換し、最初に一致したアイテムにイテレータを返します。次に、erase範囲を使用すると、最初の一致から消去され、最後に移動します。一致しない要素の順序は保持されます。

あなたが使用している場合、これはあなたのために速くうまくいくかもしれません std::vectorコンテンツの途中で消去すると、コピーや移動が多くなる可能性があります。

または、上記の答えを使用して説明することreverse_iterator::base()は興味深いものであり、知っておく価値std::removeがあります。述べた正確な問題を解決するには、より適切であると主張します。


1

何かを明確にしたかっただけです:上記のコメントと回答の一部では、消去用のポータブルバージョンが(++ i).base()として言及されています。ただし、何か不足している場合を除き、正しいステートメントは(++ ri).base()です。つまり、reverse_iterator(イテレータではない)を「インクリメント」します。

私は昨日同様のことをする必要に出くわしました、そしてこの投稿は役に立ちました。みんな、ありがとう。


0

他の人の答えを補足するために、また私がstd :: stringについて検索しているときに偶然この質問に出くわしたため、std :: string、std :: string :: erase、およびstd :: reverse_iteratorを使用して応答を返します

私の問題は、完全なファイル名文字列から画像ファイル名を消去することでした。元々はstd :: string :: find_last_ofで解決されましたが、私はstd :: reverse_iteratorを使用して別の方法を研究しています。

std::string haystack("\\\\UNC\\complete\\file\\path.exe");
auto&& it = std::find_if( std::rbegin(haystack), std::rend(haystack), []( char ch){ return ch == '\\'; } );
auto&& it2 = std::string::iterator( std::begin( haystack ) + std::distance(it, std::rend(haystack)) );
haystack.erase(it2, std::end(haystack));
std::cout << haystack;  ////// prints: '\\UNC\complete\file\'

これは、アルゴリズム、イテレータ、文字列ヘッダーを使用します。


0

逆イテレータは使いにくいです。そのため、一般的なイテレータを使用しました。'r'最後の要素から始まります。消去するものを見つけたとき。それを消去して次のイテレータを返します。たとえば、3番目の要素を削除すると、現在の4番目の要素を指します。そして新しい3位。左に移動するには1を減らす必要があります

void remchar(string& s,char c)
{      
    auto r = s.end() - 1;
    while (r >= s.begin() && *r == c)
    {
        r = s.erase(r);
        r -= 1;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.