【问题标题】:G++ compiles wrong STL codeG++ 编译错误的 STL 代码
【发布时间】:2012-01-10 15:33:45
【问题描述】:

我想我发现了一个 G++ 错误,但我不确定。我无法解释。编译不应该通过 BAD 代码,但确实如此。 g++-4.5 和 g++4.6 -std=c++0x 通过此代码而没有任何警告。

编译器认为指向 Bar 对象的指针是 Bar 对象本身。 我疯了。我花了很多时间来解决这个错误。有什么技术可以防止这种错误吗?

错误代码给出:

 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;
     }

【问题讨论】:

    标签: stl g++


    【解决方案1】:

    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(或 C++11 的 std::bind):

    #include <boost/bind.hpp>
    

    ...

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

    或 lambda 函数确实会给出您期望看到的错误。

    【讨论】:

    • 据我所知,bind2nd 的参数类型是从真实参数的类型推断出来的,然后在没有任何检查和照顾的情况下迅速转换为另一种。这是真正的陷阱:(。如下所示 char x; float f; f = *((float *)(void *)&x);
    • @DaneelS.Yaitskov - 我不太清楚为什么标准要求它是type(x) 而不是static_cast&lt;type&gt;(x),但这似乎可能是导致@987654335 的原因之一@/std::bind2nd 在 C++11 中被 std::bind 弃用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-05
    • 2014-05-25
    相关资源
    最近更新 更多