2011-01-07 8 views
2

私は、STLコンテナのセットとマップが厳密な弱い順序で要素を提供すると考えました。しかし、逆参照によって要素の値を見つけて変更することでイテレータを取得した場合、順序を復元することはできませんが、23.1.2.2および23.3.3.2に違反します。ここでは、コードセットの厳密な弱い順序を失う

int nv = 3; 
set<int> s = set<int>(); 
s.insert(5); 
s.insert(10); 
s.insert(20); 
s.insert(30); 
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit) 
    cout<<*cit<<" "; 
cout <<endl; 
set<int>::iterator it = s.find(10); 
*it = nv; 
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit) 
    cout<<*cit<<" "; 
cout <<endl; 
s.insert(40); 
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit) 
    cout<<*cit<<" "; 
cout <<endl; 

が生成されます。

5 10 20 30 
5 3 20 30 
5 3 20 30 40 

それはSTLの私のバージョン(MS VS 2008)のバグですか?または私は間違っていますか?

答えて

4

セットのiteratorとconst_iteratorは両方とも定数イテレータです。セットの値を変更することは許されず、削除と挿入だけが行われます。

+1

いいえ、上記のコードはコンパイル、リンク、実行されます – flashnik

+2

コンパイラまたはライブラリが壊れている場合のみコンパイルできます。例えば、現在のドラフト23.2.4/6: " の値の型がキーの型と同じである連想型のコンテナの場合、iteratorとconst_iteratorは両方とも定数イテレータです" –

+0

@flashnik:これはすべきですSTLまたはコンパイラの実装のいずれかでバグがあります。私の意見では、これをコンパイルすることはできません。 –

4

std::mapおよびstd::setは挿入時にのみ順序付けを行います。既存のアイテムのキー/値を変更すると、何の魔法も起こりません。その結果はおそらく未定義です。

目的の効果を得るには、元の要素を削除して新しい要素を挿入する必要があります。

+0

地図は、キーを変更することはできません。私は、次の挿入時に順序が復元されることを期待しましたが、それは起こりませんでした。結果として、このセットは永遠に無秩序を持っています:( – flashnik

+0

@flashnik:毎回木全体を並べ替えるマップのようなコンテナを書くことができますが、それは信じられないほど遅くなります。 –

+0

私はコンパイル/実行時エラーまたは上記のコードの例外:( – flashnik

0

std::mapstd::setんではないを注文厳しい弱いを提供し、彼らは 1が必要です。注文を提供する必要があります(デフォルトは、に適したstd::lessです)。

setまたはmapの要素の順序を変更すると、順序が安定していないため要件が破棄されます。

2

そのSTLバージョンのバグのようです。私のG上の

は++、私は次のエラーを取得する:あなたはイテレータを通じて連想コンテナ要素のキー部分を変更することはできません一般的なケースでは
t2.cpp:18:8: error: assignment of read-only location ‘it.std::_Rb_tree_const_iterator<_Tp>::operator* [with _Tp = int, const _Tp& = const int&]()’

+0

ありがとうございます、私はチェックするgccがありませんでした。彼らはそれをエラーとして扱うのはとても良いことです。 – flashnik

1

。コードは単純にコンパイルされません。

標準ライブラリの実装で*it = nvの割り当てが許可されている場合は、その実装の変種でなければなりません。 AFAIK、この割り当てをコンパイルできるようにすることは厳密に違法ではありません。これは実装品質の問題です。

割り当てはComeauの実装ではコンパイルされません。 MSの実装は明らかに制限が少ない。

1

std::setの要素を変更すると、内部発注に違反してデータ構造が破損する可能性があります。一部のSTL実装では、iteratorが指している変更を禁止することでこれを防ぐことができます。そのため、自分でその制約を適用する必要があります。

それはreserveを呼び出して(部屋を作るために)とポインタ演算を使用してstd::vectorの論理的な終わりを過ぎて書くようなものだ - あなたはそれを行うことができますが、それが壊れてよあなたstd::vector

あなたはセット内の要素を変更したい場合は、これは動作するはずです:

std::set<int> my_set; 

// initialize my_set ... 

std::set<int>::iterator itr = my_set.find(10); 
if (itr != my_set.end()) { 
    my_set.erase(itr++); // ++ avoids invalidating iterator 
    my_set.insert(3); 
} 

あなただけのポストインクリメントするitrをあなたがそれを使用し続けたい場合は必要です。

(ソース:効果的なSTL、 "項目22、" スコット・マイヤーズ)それが `ペア `としてイテレータを逆参照するため

+0

C++は自分の足で撃つのに最適な言語です。引用していただきありがとうございます。 – flashnik

+0

問題ありません!私は彼の本の3つすべてを強く勧めます。彼らは、C++のさまざまな面でさまざまな落とし穴を探求しています。 – Dawson

+0

私はそれらを読んだが、そのような場合は覚えていませんでした。私が言及したように、私はそのようなコードを書くことは禁じられているが、Gene Bushuyevが指摘したように、C++ 0xのみで現れ、いくつかのコンパイラでは禁じられていると指摘した。 – flashnik

関連する問題