首页 文章

成员函数指针强制转换,从Derived到Base类

提问于
浏览
2

我正在做以下事情:

  • derived class 获取带有 3 params 的成员函数指针 .

  • 使用 0 params 将其从 the base class 转换为成员函数指针 .

  • 使用 the 3 params 将其转换为 the base class .

  • 打电话给它 .

它工作正常(到目前为止),但我应该保留它吗?

当前代码说明:

EventsWarehouse用于存储和调用事件:

#include <iostream>
#include <functional>
#include <unordered_map>

class EventsWarehouse
{
public:
    typedef std::tuple<AView*, void (AView::*)()>           box_t;
    typedef std::unordered_multimap<std::string, box_t>     boxes_t;

    void        storeEvent(std::string const &event, AView *v, void (AView::*callback)())
        {
            this->_events.insert(std::make_pair(event, std::make_tuple(v, callback)));
            return ;
        }

    template<typename... Args>
    bool        fireEvent(std::string const &event, Args... args)
        {
            auto                it = this->_events.find(event);
            AView               *v;
            void                (AView::*callback_)();
            void                (AView::*callback)(Args...);

            for (; it != this->_events.end(); it++)
            {
                v = std::get<0>(it->second);
                callback_ = std::get<1>(it->second);
                /*
                ** CAST #2
                ** <void (AView::*)()>
                **  to
                ** <void (AView::*)(std::string, int, double)>
                **  before call
                */
                callback = reinterpret_cast<void (AView::*)(Args...)>(callback_);
                (v->*callback)(args...);
            }
            return (true);
        }
private:
    boxes_t         _events;

};

查看存储在上面类中的类:

class AView
{
protected:
    AView(){}
};

class DerivedView : public AView
{
public:
    void    fooCallback(std::string s, int i, double d)
        {
            std::cout << "DerivedView::fooCallback received " << s << ", " << i << ", " << d << std::endl;
            return ;
        }
};

主要:

int                         main(void)
    {
        DerivedView     dv;
        EventsWarehouse ewh;

        /*
        ** CAST #1
        ** <void (DerivedView::*)(std::string, int, double)>
        **  to
        ** <void (AView::*)()>
        **  for storing purpose
        */
        ewh.storeEvent("event 1", &dv, reinterpret_cast<void (AView::*)()>(&DerivedView::fooCallback));
        ewh.fireEvent("event 1", std::string("Hello World"), 42, 84.42);
        return (0);
    }

1 回答

  • 1

    根据草案n4296 for C 11规范,5.2.10重新解释cast [expr.reinterpret.cast]§10

    如果T1和T2都是函数类型或两种对象类型,则可以将“指向类型T1的X成员的指针”类型的prvalue显式转换为不同类型的prvalue“指向类型为T2的Y的成员的指针”.72空成员指针值(4.11)将转换为目标类型的空成员指针值 . 此转换的结果未指定,除非在以下情况下: - 将“指向成员函数的指针”类型的prvalue转换为指向成员函数类型的不同指针,并返回其原始类型,从而生成指向成员值的原始指针 . - 将“指向类型T1的X的数据成员的指针”的类型的prvalue转换为“类型为T2的Y的数据成员的指针”类型(其中T2的对齐要求不比T1的更严格)并返回到其原始类型产生指向成员值的原始指针 . 转换为指向没有参数的成员函数的指针,并返回到具有正确参数的成员函数应该返回原始指针 .

    恕我直言,问题是 fooCallback 仅在 DerivedView 类上定义,因此它是 notAView 的成员函数 .

    这是正确的:

    void (AView::*p)() = reinterpret_cast<void (AView::*)()>(&DerivedView::fooCallback);
    void (DerivedView::*callback)(std::string, int, double) =
           reinterpret_cast<void (DerivedView::*)(std::string, int, double)>(p);
    v->callback("Hello World"), 42, 84.42);
    

    提供 vAView * 指向 DerivedView

    但是当您将 void (DerivedView::*)(std::string, int, double) 转换为 void (AView::*)(std::string, int, double) 时它们是不同的类型,因此转换未指定

    它可以工作,因为非静态非虚拟成员函数的常见实现只是一个普通(非成员)函数,其隐藏参数为 this . 因此,指向成员的指针只存储该函数的地址,并使用指向 DerivedView 的指针正确调用它,从而得到预期的结果 . 但是不同的实现也可以存储实际类型并引发异常(或执行任何其他操作) .

    TL / DR:当您从 void (DerivedView::*)(std::string, int, double) 转换为 void (AView::*)(std::string, int, double) 结束时,不会将指向成员的指针强制转换为其原始类型并调用未定义的行为 .

相关问题