web-dev-qa-db-ja.com

ベクトル消去イテレーター

私はこのコードを持っています:

int main()
{
    vector<int> res;
    res.Push_back(1);
    vector<int>::iterator it = res.begin();
    for( ; it != res.end(); it++)
    {
        it = res.erase(it);
        //if(it == res.end())
        //  return 0;
    }
}

「関数呼び出しによって消去された最後の要素に続く要素の新しい場所を指すランダムアクセス反復子。これは、操作によってシーケンスの最後の要素が消去された場合のベクトル終了です。」

このコードはクラッシュしますが、if(it == res.end())を使用してから、それを返します。どうして? forループはres.end()をキャッシュするので、等しくない演算子は失敗しますか?

62
hidayat

res.erase(it)は常に次の有効なイテレータを返します。最後の要素を消去すると、.end()を指します。

ループの最後で_++it_が常に呼び出されるため、許可されていない.end()をインクリメントします。

ただし、すべての反復で常に要素をスキップするため、.end()をチェックするだけではバグが残ります(it.erase()からの戻りによって「増分」され、再びループで)

おそらく次のようなものが必要です:

_ while (it != res.end()) {
        it = res.erase(it);    
 }
_

各要素を消去する

(完全を期すために、これは単純な例であると想定しています。すべての要素を操作(削除など)を実行せずに削除したい場合は、単にres.clear()を呼び出す必要があります)

条件付きで要素のみを消去する場合、おそらく次のようなものが必要です。

_for ( ; it != res.end(); ) {
  if (condition) {
    it = res.erase(it);
  } else {
    ++it;
  }
}
_
136
Pieter
for( ; it != res.end();)
{
    it = res.erase(it);
}

または、より一般的な:

for( ; it != res.end();)
{
    if (smth)
        it = res.erase(it);
    else
        ++it;
}
27
crazylammer

Crazylammerの答えの修正として、私はよく使用します:

your_vector_type::iterator it;
for( it = res.start(); it != res.end();)
{
    your_vector_type::iterator curr = it++;
    if (something)
        res.erase(curr);
}

これの利点は、イテレータをインクリメントするのを忘れることを心配する必要がないことです。これにより、複雑なロジックがある場合にバグが発生しにくくなります。ループ内では、currはres.end()と等しくなることはなく、ベクトルから消去するかどうかに関係なく、次の要素になります。

1
Joseph Petroske

ベクトル内のメソッドeraseは、渡されたイテレータの次のイテレータを返すためです。

繰り返しの際にベクター内の要素を削除する方法の例を示します。

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};

    //method 1
    for(auto it = vecInt.begin();it != vecInt.end();){
        if(*it % 2){// remove all the odds
            it = vecInt.erase(it); // note it will = next(it) after erase
        } else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 2
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 2
    for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
        if (*it % 2){
            it = vecInt.erase(it);
        }else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 3
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 3
    vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
                 [](const int a){return a % 2;}),
                 vecInt.end());

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

}

以下の出力aw:

024
024
024

より多くの生成方法:

template<class Container, class F>
void erase_where(Container& c, F&& f)
{
    c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
            c.end());
}

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
    //method 4
    auto is_odd = [](int x){return x % 2;};
    erase_where(vecInt, is_odd);

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;    
}
1
Jayhello

イテレータを消去してからインクリメントしないでください。あなたのベクトルが奇数(または偶数、私は知らない)の数の要素を持っているなら、あなたは増加する必要はありません、あなたはベクトルの終わりを見逃します。

0
Benoit
if(allPlayers.empty() == false) {
    for(int i = allPlayers.size() - 1; i >= 0; i--)
    {
        if(allPlayers.at(i).getpMoney() <= 0) 
            allPlayers.erase(allPlayers.at(i));
    }
}

これは私のために動作します。インデックスが既に消去されていることを考える必要はありません。

0
Dawoon Yi

It ++命令は、ブロックの最後で実行されます。したがって、最後の要素を消去している場合は、空のコレクションを指している反復子をインクリメントしようとします。

0

Forループのループ式で(空の)コンテナの終わりを超えてitをインクリメントします。

0
kbjorklu

以下も動作するようです:

for (vector<int>::iterator it = res.begin(); it != res.end(); it++)
{
  res.erase(it--);
}

これに欠陥があるかどうかわからない?