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

Type erasure with function pointers does not work with GCC

我正在尝试使用一些函数指针和模板进行类型擦除,如下例所示。我觉得奇怪的是 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

这是因为你在你的对象中存储了一个引用,但是 &A::PrintA 等是临时对象,在完整表达式的末尾被销毁,所以成员引用是悬空的,它的使用导致未定义的行为。

简单的存储一份成员函数指针来修复它:

FUNC mFunc;

PS。当 mFuncs 被销毁时,唯一指针通过基指针删除子对象。除非您声明 RunBase::~RunBase virtual.

,否则行为未定义

So, who is correct and who is wrong and why ?

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