たとえば、イテレータを介して std::set の要素を変更した場合、それが「再挿入」または「再ソート」されていないことはわかっていますが、未定義の動作をトリガーするかどうかについての言及はありますか? たとえば、挿入が台無しになると思います。具体的に何が起こるかについての言及はありますか?
3 に答える
セットに保存されている値を直接編集しないでください。やや信頼できるMSDNドキュメントからこれをコピーしました。
STLコンテナクラスセットは、含まれる要素の値が一意であり、データが自動的に順序付けられるキー値として機能するコレクションからのデータの保存と取得に使用されます。セット内の要素の値を直接変更することはできません。代わりに、古い値を削除し、新しい値を持つ要素を挿入する必要があります。
なぜこれが非常に理解しやすいのか。実装には、そのset
背後にある値を変更したことを知る方法がありません。通常の実装は赤黒木です。値を変更すると、そのインスタンスのツリー内の位置が間違ってしまいます。exists
検索がツリーの間違ったブランチを下って行くために間違った結果を返すクエリなど、あらゆる種類の間違った動作が見られることが予想されます。
正確な答えはプラットフォームによって異なりますが、原則として、「キー」(セットまたは最初のタイプのマップに入れるもの)は「不変」であると想定されています。簡単に言えば、それは変更されるべきではなく、自動再挿入のようなものはありません。
より正確には、キーの比較に使用されるメンバー変数は変更しないでください。
Windows vcコンパイラは非常に柔軟性があり(VC8でテスト済み)、このコードは次のようにコンパイルされます。
// creation
std::set<int> toto;
toto.insert(4);
toto.insert(40);
toto.insert(25);
// bad modif
(*toto.begin())=100;
// output
for(std::set<int>::iterator it = toto.begin(); it != toto.end(); ++it)
{
std::cout<<*it<<" ";
}
std::cout<<std::endl;
出力は1002540ですが、これは明らかにソートされていません...悪い...それでも、このような動作は、演算子<に参加していないデータを変更する場合に役立ちます。しかし、あなたは自分が何をしているのかをよく知っています。それは、柔軟性が高すぎるために得られる代償です。
「読み取り専用の場所の割り当て」というエラーが発生するgccの動作(3.4.4でテスト済み)を好む人もいます。const_castを使用して回避できます。
const_cast<int&>(*toto.begin())=100;
これはgccでもコンパイルされており、同じ出力:1002540。しかし、少なくとも、そうすることで、おそらく何が起こっているのか疑問に思うでしょう。次に、スタックオーバーフローに移動して、このスレッドを確認してください:-)
これはできません。彼らはconst
です。内部要素に変更を加えたことをを検出できる方法は存在しないset
ため、変更することはできません。代わりに、要素を削除して再挿入する必要があります。コピーに費用がかかる要素を使用している場合は、ポインターとカスタムコンパレーターの使用に切り替える必要がある場合があります(または、右辺値参照をサポートするC ++ 1xコンパイラーに切り替えると、作業が大幅に改善されます)。