2

私のコードで最も頻繁に発生するエラーの 1 つは、ループ中に STL コンテナーが変更されることです。

ループの実行中に要素が削除または追加されるため、通常、境界外の例外が発生します。

私の for ループは通常次のようになります。

for (auto& Item : Items) { // Will not work when Items container is modified
    //... loop logic
}

複数のアイテムを削除できる場合、私はこの怪物を使用します。

for (int Index=Items.size()-1;Index<=0;Index--) {
    if (Index<Items.size()) { //Because multiple items can be removed in a single loop
        //... loop logic
    }
}

これは見栄えが悪く、2番目のオプションを使用すると気分が悪くなります。複数のアイテムを削除できる理由は、1 つのイベントで任意の数の要素を削除できるイベントによるものです。

これがいつ発生するかを示す擬似コードを次に示します。

// for each button in vector<button> {
// process button events
// event adds more buttons to vector<button>
// *ERROR* vector<button> is modified during loop.
// }

別の例として、次の項目を持つベクトルを想像してください。

// 0 1 2 3 4 5 6 7 8 9

ループを開始し、0要素ごとに進みます。では、 elementsを削除し4たいので、ここでは通常のループを使用できません。149

4

4 に答える 4

6

std::remove_ifボタンを削除する必要があるかどうかを決定する述語とともに使用します。

bool needsRemoved(const Button& button);

vec.erase(std::remove_if(vec.begin(), vec.end(), &needsRemoved), vec.end());

編集:最後の例では、二次(つまり、パフォーマンスに悪い)アルゴリズムは次のとおりです。

std::vector<int> vec = {0,1,2,3,4,5,6,7,8,9};
auto end = vec.end();
for (auto it = vec.begin(); it < end; ++it)
{
    std::set<int> bad = {1, 4, 9};
    end = std::remove_if
        (vec.begin(), end,
         [bad](int x) { return (bad.find(x) != bad.end()); });
}
vec.erase(end, vec.end());

ただし、(セットやマップなどの) 高速ルックアップを備えたコンテナーを使用する方がよいでしょう。

于 2013-06-21T08:47:20.880 に答える
3

これを確実に行うには、ほとんど 2 つの方法があります。

  1. 元のコンテナーのコピーを繰り返し処理し、元のコンテナーを操作します。実際の要素を直接格納するのではなく、コンテナがポインタを格納しない限り、これは実行できない場合があります。

  2. コンテナーの直接操作を許可しないでください。代わりに、削除する要素を何らかの方法でマークし、反復後にそれらをスイープします。新しい要素を別の一時的なコンテナーに挿入し、ループが完了した後に元の要素に追加することで、新しい要素の追加をサポートすることもできます。要素自体に「削除済み」フラグを格納する必要がないように、削除された要素を使用してこれを行うこともできます。 . addもちろん、これは適切な関数と関数で抽象化できremoveます。

編集:解決策#2の削除部分は、rectummelancoliqueで示されている消去削除イディオムでうまく行うことができます。

于 2013-06-21T08:45:23.310 に答える
0

ボタンがあるので (あまり多くないことを願っています)、各ボタンにフラグを追加して、ボタンが完全に処理されたかどうかなどを示すことができます。次に、処理されていない配列内の最初の項目を探して処理します。すべての明細が処理されるまで、これを繰り返します。

for (;;) // breaks, when all items have been processed.
{
    auto it = std::find( std::begin(Items), std::end(Items), 
        [](const Item & item){ return item.hasBeenProcessed(); }
    if ( it == std::end(Items) )
        break;
    process( *it );
}

これは安全なはずです。これは、アイテムの数に関して二次的な時間の複雑さを持つ可能性があることに注意してください。私が言ったように、アイテムが多すぎないことを願っています。これが問題になる場合は、このループを少し最適化することをお勧めします。たとえば、最後に離れた場所から検索を開始します。ただし、これは問題が発生した場合にのみ実行してください。

于 2013-06-21T08:42:38.033 に答える