22項 setとmultisetのキーのインプレース変更を避けよう

setとmultisetのキーのインプレース変更を避けよう


setやmultisetが比較に使う"キー"の値を変更すると、未定義の結果になります。

set<int> s;

s.insert(0);
*(s.begin()) = 1; //この結果は未定義

処理系によっては、"*(s.begin())"がconst T&を返すため、上のコードがコンパイルできないものがあります。
これは、"キー"の値を変更させないようにするための実装です。viausl C++ 2012はconst T&を返しました。


"*(s.begin())"がconst T&を返す処理系では、キーの値以外の値も変更できなくなるという問題が起こります。

EmpIDSet::iterator i = se.find(selectedID);
if(i != se.end()) {
    i->setTitle("Corporate Deity"); //titleは"キー"でないため変更したいが、
                    //iがconst T*なので、できない。
}

キーの値以外の値を変更するには、const_castを使います。

EmpIDSet::iterator i = se.find(selectedID);
if(i != se.end()) {
    const_cast<Employee&>(*i).setTitle("Corporate Deity");
}

もしくは、一時的なオブジェクトを利用します。

EmpIDSet se;
Employee selectedID;
...

EmpIDSet::iterator i = se.find(selectedID); // 1, 変更する要素を検索
if(i != se.end()) {
    Employee e(*i);           // 2, 要素をコピー
    se.erase(i++);             // 3, 要素を削除
    e.setTitle("Corporate Deity");   // 4, コピーを変更する
    se.insert(i, e);           // 5, 新しい値を挿入
}