使用函数指针进行类型擦除不适用于GCC

时间:2018-12-05 11:52:03

标签: c++ templates function-pointers type-erasure

我正在尝试使用一些函数指针和模板进行类型擦除,如下面的示例所示。我发现奇怪的是,与GCC相比,我得到了错误的结果,与VS 2017一样,我得到了预期的结果。那么,谁是正确的,谁是错误的,为什么呢?

COLIRU上的实时示例:http://coliru.stacked-crooked.com/a/225db5711c07c8b0

struct A { void PrintA() { std::cout << "Event A" << std::endl; } };
struct B { void PrintB() { std::cout << "Event B" << std::endl; } };
struct C { void PrintC() { std::cout << "Event C" << std::endl; } };

struct RunBase
{
    virtual void operator()() = 0;
};


template <typename T>
struct Run : public RunBase
{
    using FUNC = void (T::*)();
    Run(T& o, FUNC&& f) : mObj(o), mFunc(f) {}
    void operator()() override { (mObj.*mFunc)(); }

    T& mObj;
    FUNC& mFunc;
};

int main()
{
    A a; 
    B b; 
    C c; 

    std::vector<std::unique_ptr<RunBase> > mFuncs;
    mFuncs.push_back(std::make_unique<Run<A> >(a, &A::PrintA));
    mFuncs.push_back(std::make_unique<Run<B> >(b, &B::PrintB));
    mFuncs.push_back(std::make_unique<Run<C> >(c, &C::PrintC));

    for (auto& i : mFuncs)
        (*i)();

  return 0;
}

预期结果:

Event A
Event B
Event C

但GCC给了我

Event C
Event C
Event C

1 个答案:

答案 0 :(得分:3)

这是因为您将引用存储在对象内部,但是&A::PrintA等是临时变量,它们在完整表达式的末尾被销毁,因此成员引用处于悬空状态,使用它会导致未定义的行为

只需存储成员函数指针的副本即可对其进行修复:

FUNC mFunc;

PS。当mFuncs被销毁时,唯一指针通过基本指针删除子对象。除非您声明RunBase::~RunBase为虚拟,否则行为是不确定的。


  

那么,谁是正确的,谁是错误的,为什么?

您的程序有误;没有正确的行为。