2012-01-10 153 views
1

我想我已經找到了一個G ++的錯誤,但我不確定。我無法解釋它。編譯不應該通過BAD代碼,但它確實。 g ++ - 4.5和g ++ 4.6 -std = C++ 0x在沒有任何警告的情況下傳遞此代碼。G ++編譯錯誤的STL代碼

由於編譯器認爲指向Bar對象的指針是Bar對象本身。 我瘋了。我花了很多小時纔得到這個bug。有沒有什麼技術可以防止這種bug?

爲代碼給出:

g++-4.6 for_stackoverflow.cpp && ./a.out 
address of bar in main() 0xbff18fc0 
Foo 0x9e80008  Bar  0xbff18fec 
Foo 0x9e80028  Bar  0xbff18fec 
Foo 0x9e80048  Bar  0xbff18fec 
end 

源代碼:

 #include <iostream> 
    #include <list> 
    #include <iomanip> 
    #include <algorithm> 

    #define BAD 

    using namespace std; 

    class Bar; 

    class Foo { 
    public: 
     virtual void tick(Bar & b) { 
     cout << "Foo " << this << "  Bar " << setw(14) << (&b) << endl; 
     }  
    }; 

    class Bar : public list<Foo*> { 
    }; 

    int main() { 
     Bar bar; 
     cout << "address of bar in main() " << &bar << endl; 
     bar.push_back(new Foo()); 
     bar.push_back(new Foo()); 
     bar.push_back(new Foo()); 
    #ifdef GOOD 
     for_each(bar.begin(), bar.end(), bind2nd(mem_fun(&Foo::tick), bar)); 
    #elif defined(BAD) 
     for_each(bar.begin(), bar.end(), bind2nd(mem_fun(&Foo::tick), &bar)); 
    #else 
    #error "define GOOD xor BAD" 
    #endif 
     cout << "end" << endl; 
     return 0; 
    } 

回答

0

bind2nd被聲明爲:

template <class Fn, class T> 
binder2nd<Fn> bind2nd(const Fn&, const T&); 

這意味着該類型T推導,在這種情況下作爲Bar *

在我的系統它的實現爲:

template<typename _Operation, typename _Tp> 
inline binder2nd<_Operation> 
bind2nd(const _Operation& __fn, const _Tp& __x) 
{ 
    typedef typename _Operation::second_argument_type _Arg2_type; 
    return binder2nd<_Operation>(__fn, _Arg2_type(__x)); 
} 

要明白爲什麼會編譯考慮:

class Bar {}; 

int main() { 
    Bar *b = 0; 
    typedef const Bar& type; 
    const type t = type(b); 
} 

這似乎是真正的問題,並與G ++編譯,因爲it's basically a reinterpret_cast

最簡單的解決方法是改變它使用boost::bind(或std::bind爲C++ 11):

#include <boost/bind.hpp> 

...

boost::bind(mem_fun(&Foo::tick), _1, &bar) 

或λ函數得到誤差你會期待看到。

+0

據我所知,bind2nd的一個參數類型是從一個真實參數的類型中推斷出來的,然後在沒有任何檢查和關心的情況下將其急劇轉換爲另一個類型。這是真正的陷阱:(如下所示:char x; float f; f = *((float *)(void *)&x); – 2012-01-10 19:16:02

+0

@ DaneelS.Yaitskov - 我不太清楚爲什麼標準要求它是'type (x)'而不是'static_cast (x)',但這可能是導致std :: bind1st/std :: bind2nd被棄用的原因之一。 '在C++ 11中 – Flexo 2012-01-10 19:18:57