14

ブーストまたはコンパイラが最後に責任を負うべきであることは知っていますが、ここには別の説明がありません。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

4

2 に答える 2

2

/Oy- コンパイラ フラグを追加してみてください ([構成プロパティ] -> [C/C++] -> [最適化] -> [フレーム ポインタの省略] を無効にします)。

私は MSVC 2010 を使用して同じ問題を抱えていましたが、これで解決しました!

于 2013-01-10T18:30:31.087 に答える
1

テンプレート関数のデフォルト値に関するVC ++のこのバグのように見えます。

2009 年 8 月に非常によく似たバグがここにあり (次のリリースで M$ によって「修正済み」として閉じられました)... さまざまな点で一貫しています: VC++ 固有であり、GCC で動作し、デフォルトのテンプレート引数で断続的なエラーが発生します (コンパイル時の問題はありません)、問題は 2 回目のインスタンス化でのみ発生します。

とは言っても、コンパイラの出力や魔法の 10 ループについて説明することはできません... :-)

VC++ には、テンプレートを使用した回避策に関する古い記事もあります。最近非常によく似たバグがあり、あなたのバグと Color_of_Green のバグがどれほど一貫しているように見えるかというと、おそらく Boost ではなく VC++ です。

私の推測?これらの署名で窒息しています:const T & data = T()graph_elements_collection.hで。MSFT は、これを に変更することをお勧めしconst T data = T()ます。それがこのコンパイラのバグに関連していることを確認するには、それを試すか、MSFT によって投稿された回避策を試してください...ここ...

于 2010-05-30T14:51:56.450 に答える