- 追加された行はこの色です。
- 削除された行はこの色です。
[[Hirofumi Fujii Start Page]]
06-Aug-2009 追記:
g++ (GCC) 3.4.5 (mingw-vista special r3) では
std::list<MyClass>::iterator it = store.end();
--it;
while(it-- != itcur)
store.pop_back();
は OK であるが、Visual Studio 2008 の VC++ では store.size() == 1 の
時、 decrement できない旨のエラーを引き起こす。it-- に対し、
"decrement した値も作っておいてから" 比較を行っているようだ。
つまり、it が store.begin() の時には、比較の前に
decrement 操作をしてしまってエラーを引き起こすらしい。うーん、
文法上はどうなんだろう。
---------[06-Aug-2009 の追記、ここまで]------------
13-Aug-2009 更に追記:
よく見たら、上の例
std::list<MyClass>::iterator it = store.end();
--it;
while(itcur != it--)
store.pop_back();
と同じなのかどうか?どちらの例も、そもそも while 文を抜けた後、
it は itcur に一致しているべきなのか、それとも itcur-- に一致
しているべきなのか(比較して抜けることを決めた後 decrement を
実行してから抜けるのか)。うーん。こんな code 書くべきでは
ないというのが正解か?
---------[13-Aug-2009 の追記、ここまで]------------
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 にはならなかった。そうでなくても、
it-- として、bug にはならなかった(これも嘘であることが
判明。上記追記を見よ。)。そうでなくても、
while(it != itcur)
{
--it; store.pop_back();
}
としていれば、bug にはならない。
ここまで書けば理由はわかるだろう。
pop_back した時点で it が無効になっているからで、list を普通に
考えて実装すれば iterator の指し示す要素の中には、要素本体の他に
次要素と前要素の iterator(pointer) も格納しておくだろう。
ところが、その要素そのものが消えているので、++ も -- も、
もはや無効というわけである。上記のことをやりたければ、
pop_back する"前に" decrement した値を得ておくべきである。
pop_back() が it の ++ や -- に作用するなどというのは、コードを
見ただけではなかなか読み取れない。erase() を使えばという意見も
あるかも知れないが、似たようなものだと思う。なぜなら、erase() を
使って書き換えてみたのだが、気がつかなかったから。単に私が馬鹿だと
いう意見なら、その通り。
なお、実装によっては上記最初のコードでも動くこともあるだろう。
pop_back() した時点で、it が end() に一致するような実装も可能と
思われ、その場合にはうまくいくはずである。多分、私が用いている
実装では end() は特別扱いだと思われる(固定している可能性大。
双方向 list なので、効率を考えるとその方がよいかも)。