web-dev-qa-db-ja.com

ループ内のベクトルの要素を削除する

この質問と似たような質問があることは知っていますが、彼らの助けを借りて自分のコードの道を見つけることができませんでした。ループ内でこの要素の属性をチェックすることで、ベクトルの要素を単に削除/削除したいだけです。どうやってやるの?次のコードを試しましたが、あいまいなエラーメッセージが表示されます。

「operator =」機能は「Player」では使用できません。

 for (vector<Player>::iterator it = allPlayers.begin(); it != allPlayers.end(); it++)
 {
     if(it->getpMoney()<=0) 
         it = allPlayers.erase(it);
     else 
         ++it;
 }

私は何をすべきか?

更新:質問 vector :: erase with pointer member は同じ問題に関係すると思いますか?したがって、割り当て演算子が必要ですか?どうして?

63
arjacsoh

itループでforをインクリメントしないでください:

_for (vector<Player>::iterator it=allPlayers.begin(); 
                              it!=allPlayers.end(); 
                              /*it++*/) <----------- I commented it.
{

   if(it->getpMoney()<=0) 
      it = allPlayers.erase(it);
  else 
      ++it;
 }
_

itがfor-body自体でインクリメントされるため、コメント部分; _it++_は必要ありません。

エラー「 'operator =' function is in available in 'Player'」については、erase()の使用に起因します。内部で_operator=_を使用してベクター内の要素を移動します。 erase()を使用するには、クラスPlayerのオブジェクトが割り当て可能である必要があります。つまり、Playerクラスに_operator=_を実装する必要があります。

とにかく、避ける必要があります rawループ1 可能な限り、代わりにアルゴリズムを使用することを選択する必要があります。この場合、人気のある Erase-Remove Idiom を使用すると、作業を簡素化できます。

_allPlayers.erase(
    std::remove_if(
        allPlayers.begin(), 
        allPlayers.end(),
        [](Player const & p) { return p.getpMoney() <= 0; }
    ), 
    allPlayers.end()
); 
_

1.それは Sean Parentによる最高の話 の1つです。

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

これはベクター内の要素を削除する私の方法です。理解しやすく、トリックは必要ありません。

13
Dawoon Yi

ループを忘れて、stdまたはboost範囲アルゴリズムを使用します。
LambdaでBoost.Rangeを使用すると、次のようになります。

boost::remove_if( allPlayers, bind(&Player::getpMoney, _1)<=0 );
11
TimW

具体的な問題は、Playerクラスに代入演算子がないことです。 「プレイヤー」をベクターから削除するには、コピー可能または移動可能にする必要があります。これは、ベクトルが連続している必要があるため、要素を削除するときに作成されたギャップを埋めるために要素を並べ替える必要があるためです。

また:

Stdアルゴリズムを使用

allPlayers.erase(std::remove_if(allPlayers.begin(), allPlayers.end(), [](const Player& player)
{
    return player.getpMoney() <= 0;
}), allPlayers.end());

ブーストがある場合はさらに簡単です:

boost::remove_erase_if(allPlayers, [](const Player& player)
{
    return player.getpMoney() <= 0;
});

C++ 11ラムダのサポートがない場合は、TimWの回答を参照してください。

5
ronag

または、逆方向にループします。

for (vector<Player>::iterator it = allPlayers.end() - 1; it != allPlayers.begin() - 1; it--)
    if(it->getpMoney()<=0) 
        it = allPlayers.erase(it);
4
hhhhhhhhh

C++ 11は、ここで使用する新しい関数のコレクションを導入しました。

allPlayers.erase(
    std::remove_if(allPlayers.begin(), allPlayers.end(),
        [](auto& x) {return x->getpMoney() <= 0;} ), 
    allPlayers.end()); 

そして、エンド要素をそれほどシフトする必要がないという利点が得られます。

3
UKMonkey

遅い答えですが、非効率的なバリアントを見たとして:

  1. std::remove または std::remove_ifは進むべき道です。
  2. 何らかの理由でそれらが利用できない場合、または他の理由で使用できない場合は、これらがあなたから隠れるようにします。

要素を効率的に削除するためのコード:

auto pos = container.begin();
for(auto i = container.begin(); i != container.end(); ++i)
{
    if(isKeepElement(*i)) // whatever condition...
    {
        *pos++ = *i; // will move, if move assignment is available...
    }
}
// well, std::remove(_if) stops here...
container.erase(pos, container.end());

このようなループを明示的に記述する必要があるかもしれませんe。 g。エレメント自体を削除するかどうかを判断するためにイテレーター自体が必要な場合(条件パラメーターはエレメントへの参照を受け入れる必要がありますか?)。 g。後継者/前任者との特定の関係により(ただし、この関係が平等である場合、 std::unique )。

0
Aconcagua