std::setのinsertが返すiteratorについて
std::setに要素を追加するコードとして以下のようなものを書いてみた.
#include <iostream> #include <set> struct Element { Element(int const& arg1, int const& arg2) : first(arg1), second(arg2) {} int first; int second; }; inline bool operator<(Element const& lhs, Element const& rhs) { return lhs.first < rhs.first; } int main() { std::set<Element> container; Element elem1(0, 1); container.insert(elem1); std::cout << "fisrt=" << elem1.first << ", second=" << elem1.second << std::endl; std::cout << "fisrt=" << (*container.begin()).first << ", second=" << (*container.begin()).second << std::endl; }
これはちゃんと動作して, "fisrt=0, second=1"のような結果が得られる.
次に要素を挿入すると同時にsecondの値を書き換えるようにコードを変更してみる. この際, 挿入してから再度要素を見つけ出すのは無駄だと考え, insertの返り値を利用する.
std::pair<std::set<Element>::iterator, bool> ret(container.insert(elem1)); (*ret.first).second = 10;
http://www.cplusplus.com/reference/set/set/insert/
しかしながら, これは通らない. insertはiteratorを返すと書いてあるにも関わらず, const_iteratorが返ってきているようである. 実際その通りで, std::setではiteratorは実質const_iteratorであるらしい. 良く考えると当然の道理でここでiteratorに対して値を変更されるとstd::set内での順序がおかしくなる恐れがあるからである.
逆に言えば今回の場合, 変更を試みたメンバ変数secondはElementの順序関係に影響を及ぼさないため, この限りではないと言える. その場合に限ってmutableを使えば問題ない.
http://www.geocities.jp/ky_webid/cpp/language/020.html
struct Element { Element(int const& arg1, int const& arg2) : first(arg1), second(arg2) {} int first; mutable int second; };
これで問題なく動作する.
$ g++ main.cpp && ./a.out fisrt=0, second=1 fisrt=0, second=10
mutableをつけたことでconst_iteratorに対してでもsecondが変更可能になった. こうなってくるとconstって何だ?という気持ちにもなるが, 初めてmutableを使う場面に出くわしたので記録しておく.
http://d.hatena.ne.jp/matobow/20111129/p1
蛇足だが, Elementのfirstの方をmutableにして, 下記のコードを実行すると順序がおかしな事になっていることがわかる.
int main() { std::set<Element> container; Element elem1(1, 1), elem2(2, 2); container.insert(elem1); std::pair<std::set<Element>::iterator, bool> ret(container.insert(elem2)); (*ret.first).first = 0; for (std::set<Element>::const_iterator i(container.begin()); i != container.end(); ++i) { std::cout << "fisrt=" << (*i).first << ", second=" << (*i).second << std::endl; } }
$ g++ main.cpp && ./a.out fisrt=1, second=1 fisrt=0, second=2
なんとも気持ちが悪いが, 仕方が無いのかもしれない.