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

Member function pointer cast, from Derived to Base class

本文关键字:派生 基类 转换 函数 指针 成员      更新时间:2023-10-16

我正在做以下事情:

  • 派生类获取具有3个参数的成员函数指针
  • 基类转换为成员函数指针,0参数
  • 的3个参数返回给基类
  • 调用。

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

当前代码描述:

eventwarehouse用于存储和调用事件:

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

根据c++ 11规范草案n4296, 5.2.10 Reinterpret cast [expr.reinterpret]演员]§10

如果T1和T2都是函数类型或都是对象类型,则"指向T1类型X的成员的指针"类型的右值可以显式地转换为另一类型"指向T2类型Y的成员的指针"的右值的空成员指针值(4.11)转换为目的类型的空成员指针值。此转换的结果是未指定的,除了以下情况:

-将"成员函数指针"类型的右值转换为另一个成员函数指针类型和返回到其原始类型将产生指向成员值的原始指针。

-将类型为"指向T1类型X的数据成员的指针"的右值转换为类型为"指向数据的指针"T2型Y的成员"(其中T2的对中要求并不比T1严格)返回到其原始类型将产生指向成员值的原始指针。从指向没有形参的成员函数的指针转换到具有正确形参的成员函数的指针,应返回原始指针。

IMHO,问题是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的指针,给出了预期的结果。但是另一种实现也可以存储实际类型并引发异常(或做其他任何事情)。

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