[[Hirofumi Fujii Start Page]]
C++ の std::list で久々に原因究明に半日を要した bug を書いてしまった。
線形 list の途中から先を最後尾から削除するのに、
std::list<MyClass> store;
std::list<MyClass>::iterator itcur;
:
std::list<MyClass>::iterator it = store.end();
--it;
while(it != itcur)
{
store.pop_back(); --it;
}
みたいなコードを書いてしまった。itcur は現在地を示して
いる iterator で、最後尾(end() の一つ前)から消していって、
現在地まで来たらやめる(現在地は残す)つもりのコードである。
ものの見事にアクセス違反で落ちるコードである。--it を
最後に持ってきているのは、消す前に it を使って、消す要素の
情報にアクセスしているからで、そうでなければ while 文中で
it-- として、bug にはならなかった。そうでなくても、
while(it != itcur)
{
--it; store.pop_back();
}
としていれば、bug にはならない。
ここまで書けば理由はわかるだろう。
pop_back の時点で it が無効になっているからで、list を普通に
考えて実装すれば iterator の指し示す要素の中に次要素と
前要素の iterator(pointer) を格納しておくだろう。
ところが、その要素そのものが消えているので、++ も -- も、
もはや無効というわけである。
pop_back() が it の ++ や -- に作用するなどというのは、コードを
見ただけではなかなか読み取れない。erase() を使えばという意見も
あるかも知れないが、似たようなものだと思う。なぜなら、erase() を
使って書き換えてみたのだが、気がつかなかった。単に私が馬鹿だと
いう意見なら、その通り。