ブーストまたはコンパイラが最後に責任を負うべきであることは知っていますが、ここには別の説明がありません。msvc 2008 SP1 とブースト 1.43 を使用しています。
次のコード スニペットでは、実行によって3 番目の BOOST_FOREACH ループが終了することはありません
typedef Graph<unsigned, unsigned>::VertexIterator Iter;
Graph<unsigned, unsigned> g;
g.createVertex(0x66);
// works fine
Iter it = g.getVertices().first, end = g.getVertices().second;
for(; it != end; ++it)
;
// fine
std::pair<Iter, Iter> p = g.getVertices();
BOOST_FOREACH(unsigned handle, p)
;
// fine
unsigned vertex_count = 0;
BOOST_FOREACH(unsigned handle, g.getVertices())
vertex_count++;
// oops, infinite loop
vertex_count = 0;
BOOST_FOREACH(unsigned handle, g.getVertices())
vertex_count++;
vertex_count = 0;
BOOST_FOREACH(unsigned handle, g.getVertices())
vertex_count++;
// ... last block repeated 6 times
イテレータ コード:
class Iterator
: public boost::iterator_facade<Iterator, unsigned const,
boost::bidirectional_traversal_tag>
{
public:
Iterator()
: list(NULL), handle(INVALID_ELEMENT_HANDLE)
{}
explicit Iterator(const VectorElementsList &list, unsigned handle = INVALID_ELEMENT_HANDLE)
: list(&list), handle(handle)
{}
friend std::ostream&
operator<<(std::ostream &s, const Iterator &it)
{
s << "[list: " << it.list <<", handle: " << it.handle << "]";
return s;
}
private:
friend class boost::iterator_core_access;
void increment()
{
handle = list->getNext(handle);
}
void decrement()
{
handle = list->getPrev(handle);
}
unsigned const& dereference() const
{
return handle;
}
bool equal(Iterator const& other) const
{
return handle == other.handle && list == other.list;
}
const VectorElementsList<T> *list;
unsigned handle;
};
ASM の楽しみ:
vertex_count = 0;
BOOST_FOREACH(unsigned handle, g.getVertices())
// initialization
013E1369 mov edi,dword ptr [___defaultmatherr+8 (13E5034h)] // end iterator handle: 0xFFFFFFFF
013E136F mov ebp,dword ptr [esp+0ACh] // begin iterator handle: 0x0
013E1376 lea esi,[esp+0A8h] // begin iterator list pointer
013E137D mov ebx,esi
013E137F nop
// forever loop begin
013E1380 cmp ebp,edi
013E1382 jne main+238h (13E1388h)
013E1384 cmp ebx,esi
013E1386 je main+244h (13E1394h)
013E1388 lea eax,[esp+18h]
013E138C push eax
// here iterator is incremented in ram
013E138D call boost::iterator_facade<detail::VectorElementsList<Graph<unsigned int,unsigned int>::VertexWrapper>::Iterator,unsigned int const ,boost::bidirectional_traversal_tag,unsigned int const &,int>::operator++ (13E18E0h)
013E1392 jmp main+230h (13E1380h)
vertex_count++;
// forever loop end
イテレータ ハンドルが EBP にキャッシュされ、イテレータ operator++() 関数の呼び出しにもかかわらずインクリメントされないことは簡単にわかります。
Itarator の実装を std::iterator から派生したものに置き換えましたが、問題は解決していないため、これは iterator_facade の障害ではありません。この問題は、msvc 2008 SP1 x86 および amd64 リリース ビルドにのみ存在します。msvc 2008 でのデバッグ ビルド、および msvc 2010 でのデバッグ/リリース ビルドと gcc 4.4 (Linux) は正常に動作します。さらに、BOOST_FOREACH ブロックを正確に 10 回繰り返す必要があります。9回繰り返せばOKです。
BOOST_FOREACH のテンプレート トリック (const auto_any) の使用により、コンパイラはイテレータ ハンドルが定数であると想定し、実際の値を再度読み取ることはないと思います。
私のコードが間違っていると聞いてとてもうれしく思います。それを修正し、BOOST_FOREACH に進みます (BOOST_FOREVER とは対照的に :)。
関連する可能性があります: BOOST_FOREACH が C++ 文字列で時々機能しないのはなぜですか?
編集:
問題を再現する単純化されたプロジェクトを用意しました。テンプレートも、デフォルトのパラメーターも、何もありません。ここから入手してください: http://yabcok.nazwa.pl/ugly3.zip