成员函数指针转换,从派生类到基类

3
我正在做以下事情:
  • 从一个派生类中获取一个带有3个参数的成员函数指针。
  • 将其转换为来自基类的带有0个参数的成员函数指针。
  • 将其转换回具有3个参数的基类。
  • 调用它。
目前它可以正常工作,但我应该保留它吗?
当前代码的描述:
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);
    }

4
你的问题是什么? - rozina
1
@rozina 我认为隐含的问题是“它似乎能工作,但我所描述的方式是否可行,或者可能会引发问题?” - Tim Seguine
1个回答

3
根据C++11规范草案n4296,5.2.10重新解释转换[expr.reinterpret.cast] §10
如果T1和T2都是函数类型或对象类型,则X类型的成员指针的prvalue可以明确转换为不同类型“Y类型的成员指针 of type T2”。null成员指针值(4.11)转换为目标类型的null成员指针值。这种转换的结果未指定,但以下情况除外:
将类型为“指向成员函数的prvalue”显式转换为不同的成员函数指针类型,然后将其转回到原始类型会产生原始的成员指针值。
将类型为“X类型T1数据成员的指针”转换为“Y类型T2数据成员的指针”(其中T2的对齐要求不严格于T1的对齐要求),然后将其转换回其原始类型会产生原始的成员指针值。 将其转换为无参数的成员函数指针,然后再转回具有正确参数的成员函数应返回原始指针。
在我看来,问题在于fooCallback仅在DerivedView类上定义,因此它不是AView类的成员函数。
这是正确的:
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);

假设v是指向DerivedViewAView *

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

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

简而言之:当你将void (DerivedView::*)(std::string, int, double)转换为void (AView::*)(std::string, int, double)时,你没有将指针转换回其原始类型并调用未定义行为。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接