2016-02-08 22 views
7

標準で明示的にコンテナの変更をstd::for_each以内に禁止していますか?std :: for_each内のコンテナを変更する

具体的には、std::listの場合、リストが変更されるとイテレータは無効になりません。したがって、次のコードが動作します。

std::list<int> list; 

list.push_front(5); 
list.push_front(10); 
auto it = list.end(); 
it--; // point to 5 

std::for_each(list.begin(), list.end(), [&](int i){ 
    /* the line below will remove the last element in the list; 
    * list will have only one element (the currently processed one); 
    * list.end() is not invalidated and we exit for_each() */ 
    list.erase(it); 
}); 

これは間違いなく悪いコードです。しかしそれは合法ですか?

+0

この質問は、もっともっと上げられるはずです。本当に... –

答えて

3

標準は、明示的にstd::for_each内のコンテナを修正禁止していますか?がf正確last - first回を適用します:私はそれを考えることができ

唯一のことは、[alg.foreach]に、我々は

複雑さを持っていることではない、標準に準拠しているこのコードになるだろう。

fである。for_eachが適用される。

リストが変更され、要素が削除されたため、私たちはもはやその複雑さを満たさなくなりました。 std::for_each内標準は、明示的にコンテナを修正禁止していfor_each

-1

はい、合法です。基本となるコンテナを変更しないことが保証されているものが必要な場合は、const itersがあります。

+1

証明? 'std :: list'でない場合、または現在処理されている要素のイテレータを削除すると、クラッシュします。 – Kane

+1

legal!=クラッシュしない –

+0

"legal"を定義してください。私は、このコードがスタンダードに準拠しているのか、それともちょうどうまく動作するのでしょうか? – Kane

2

あなたのコードは合法的な境界線です。

投稿コードでは、偶然で合法です。 1つ以上の項目をリストに追加すると、プログラムがクラッシュします。

問題を参照するには、以下試してみてください。

int main() 
{ 
    std::list<int> list; 

    list.push_front(5); 
    list.push_front(10); 
    list.push_front(12); 
    auto it = list.end(); 
    it--; // point to 5 

    std::for_each(list.begin(), list.end(), [&](int i){ 
       /* the line below will remove the last element in the list; 
        * list will have only one element (the currently processed one); 
        * list.end() is not invalidated and we exit for_each() */ 
       list.erase(it); 
       }); 
} 
+0

はい、同じイテレータを2回消去しますが、これは許可されていません。しかし、コンテナを変更することは概念的に許されているのでしょうか、それとも何らかのUBを引き起こすのでしょうか? – Kane

+0

eraseを最初に呼び出すとイテレータが無効になるため、同じイテレータを使用して2回eraseを呼び出すのはUBです。 –

+0

はい、それは私が求めていることではありません。反復子が2回消去されないことが保証されているとします。それはまだUBですか? – Kane

3

を使用しながら、それはそれ不適合作る場合、私は知らないが、それは、私はそれはあなたがコンテナから要素を削除することはできないだろう見ることができる唯一のものです?

いいえ、は明示的にありません。少なくとも「明示的」という賢明な定義については。 std::for_each(§25.2.4)のセクションでは、繰り返し範囲の変更を引き起こす反復子無効化または副作用について何も言及していません。

しかし、暗黙的にのコードが機能しない理由がたくさんあります。 std::for_eachの実装方法を検討してください。それは完全に汎用的なので、それが動作するstd::listについて何も知らない。範囲を形成するために2つのイテレータを与えました。つまり、lastにはfirstから到達できます。 std::for_eachは、関数オブジェクトがfirstからlastになるローカルイテレータオブジェクトを無効にしたことをどのように知るべきですか?

機能が無効化が発生したことが分かっていても、どうすればよいですか?手書きのループでは、eraseの結果を受け取り、繰り返しを続けるでしょうが、std::for_eachはそれを定義で行うことはできません。イテレータや アルゴリズムはどのような場合で悲惨であると連携のシーケンスを無効にする、例えば

http://www.drdobbs.com/effective-standard-c-library-foreach-vs/184403769

それは言う:

はここで古いが、まだ有効な参照の記事です。 関数for_eachにも、 標準がそうしていなくても、この常識規則に従わなければなりません。


私は一度読んでいくつかの賢明な言葉を言い換え:

C++標準は、人間によって書かれています。私たちが望むようにいつも完璧というわけではありません。

2

25.2.4/2を:

効果:最初及び進行から最後まで起動、)最初、最後 範囲内のすべての反復子を間接参照の結果にfを適用 - 1

その後25.2.4/4:

複雑さ:まさに最後のfを適用 - 最初の回。

私は、これらの2つの点が、要素の数を変更する方法でコンテナを突然変異させることを明示的に禁止していると主張しています。

つまり、たとえリストに項目が1つでもあると失敗しても、関数を呼び出す前に標準ライブラリの実装がイテレータをインクリメントしていても(そして、そのような実装)。

関連する問題