成员函数指针的转换,从Derived到Base类 [英] Member function pointer cast, from Derived to Base class

查看:133
本文介绍了成员函数指针的转换,从Derived到Base类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在执行以下操作:


  • 通过 3个参数来获取成员函数指针 strong>派生的类。

  • 将其从基类(具有 0个参数)投射到成员函数指针。

  • 将其三个参数放回基类

  • 调用它。

  • Taking a member function pointer with 3 params from a derived class.
  • Casting it to a member function pointer from the base class with 0 params.
  • Casting it to the base class with the 3 params back.
  • Calling it.

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

It works fine (so far), but should i keep it?

当前代码的描述:

EventsWarehouse用于存储和调用事件:

EventsWarehouse is used to store and invoke events:

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

};

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

View classes stored in the above class:

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的n4296草案++ 11规范5.2.10重新解释类型转换[expr.reinterpret.cast]§10

According to draft n4296 for C++11 specification, 5.2.10 Reinterpret cast [expr.reinterpret.cast] §10


类型为指向成员的指针的prvalue如果T1和T2均为函数类型或均为对象类型,则可以将T1类型的X显式转换为其他类型的指向T2类型的Y的成员的指针的prvalue。72
空成员指针值( 4.11)转换为目标类型的空成员指针值。除以下情况外,该转换的结果未指定:

A prvalue of type "pointer to member of X of type T1" can be explicitly converted to a prvalue of a different type "pointer to member of Y of type T2" if T1 and T2 are both function types or both object types.72 The null member pointer value (4.11) is converted to the null member pointer value of the destination type. The result of this conversion is unspecified, except in the following cases:

-将类型为指向成员函数的指针的prvalue转换为指向成员函数$ b的不同指针$ b类型并返回其原始类型将产生指向成员值的原始指针。

— converting a prvalue of type "pointer to member function" to a different pointer to member function type and back to its original type yields the original pointer to member value.

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

— converting a prvalue of type "pointer to data member of X of type T1" to the type "pointer to data member of Y of type T2" (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer to member value. the conversion to a pointer to member function with no parameters and back to a member functions with correct parameters should give back original pointer.

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

IMHO, the problem is that fooCallback is only defined on DerivedView class, and as such it is not a member function of class 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 AView * 指向 DerivedView

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

But as you end converting a void (DerivedView::*)(std::string, int, double) to a void (AView::*)(std::string, int, double) they are different type so conversion is unspecified

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

It works, because common implementation for non static non virtual member functions is simply a normal (non member) function with a hidden parameter being this. So the pointer to member just stores the address of that function and correctly calls it with a pointer to a DerivedView giving expected result. But a different implementation could also store the actual type and raise an exception (or do anything else).

TL / DR:最后,您从<$ c进行了转换$ c> void(DerivedView :: *)(std :: string,int,double)到 void(AView :: *)(std :: string,int,double )您不会将其成员强制转换为指向其原始类型的指针并调用未定义的行为。

TL/DR: As you end with a conversion from void (DerivedView::*)(std::string, int, double) to void (AView::*)(std::string, int, double) you do not cast to pointer to member to its original type and invoke undefined behaviour.

这篇关于成员函数指针的转换,从Derived到Base类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆