向上转换函数指针是否安全?

时间:2013-02-19 15:38:16

标签: c++ member-function-pointers upcasting

我有基类ObjectEvent

class Object
{
//...
};

class Event
{
};

函数指针的typedef

typedef void (Object::*PF) (Event*);

一个存储两个指针的不相关的类

class Dispatcher
{
public:
    Dispatcher (Object* obj, PF pf) : _obj (obj), _pf (pf)

    void call (Event* event)
    {
        _obj->*pf (event);
    }

    Object* _obj;
    PF _pf;
};

然后我有一个具体的对象和一个具体的事件

class ConcreteEvent : public Event
{
};

class ConcreteObject : public Object
{
public:
   void handle (ConcreteEvent* event)
   {
      // Do something specific for ConcreteEvent
   }
};

然后像这样称呼它

ConcreteObject* obj = new ConcreteObject();
Dispatcher dispatcher (obj, static_cast<PF>(&ConcreteObject::handle));

ConcreteEvent* event = new ConcreteEvent ();
dispatcher.call (event);

我保证将始终使用正确的事件调用调度程序,即,当它封装的函数指针实际需要ConcreteEvent时,我不会调用调度程序并将其传递给SomeOtherConcreteEvent

问题是:这可以保证有效吗?在linux和mingw上,gcc 4.7当然可行。

2 个答案:

答案 0 :(得分:4)

从C ++ 11标准,第4.11.2节:

  

类型为“指向cv T类型B的成员的指针”的prvalue,其中B是类类型,可以转换为   类型为“指向cv T类型D的成员的指针”的prvalue,其中D是B的派生类(第10条)。如果B是   D的不可访问(第11条),模糊(10.2)或虚拟(10.1)基类,或虚拟的基类   D的基类,需要这种转换的程序是不正确的。转换的结果是指   转换发生前与指向成员的指针相同的成员,但它指的是基数   类成员,好像它是派生类的成员。

所以是的,这应该是安全的。

编辑:所以如果你真的想要向下转换:这也是合法的,根据C ++ 11 5.2.9.12:

  

类型为“指向cv1 T类型D的成员的指针”的prvalue可以转换为类型为“指针”的prvalue   B的类型cv2 T的成员,其中B是D的基类(第10条),如果有效的标准转换来自   “存在类型T的B成员的指针”到“指向类型T的D的成员的指针”存在(4.11),并且cv2是相同的   cv-quali fi cation,或者比cv1更高的cv-quali fi cation。   69

答案 1 :(得分:1)

我认为是安全的,原因有两个。

第一个是指向成员函数的指针只能安全地传播向下(因为Derived类必然从基类继承该函数,而反之则不然)

class Base {
public:
   void foo();
}; // class Base

class Derived: public Base {
public:
   void bar();
};

using PBase = void (Base::*)();
using PDerived = void (Derived::*)();

int main() {
   PBase pb = &Base::foo;
   PDerived pd = &Derived::bar;

   pb = pd; // error: cannot convert 'PDerived {aka void (Derived::*)()}'
            //                    to 'PBase {aka void (Base::*)()}' in assignment
   pd = pb;
}

(见here

第二个是你不能像那样改变参数的类型。要说明问题,请使用ConcreteObject: public virtual Object,您会发现它无法正常工作。


现在,这并不意味着你想做的事情是不可能的,只是它需要多一点

理想情况下,您只需修复签名即可同时使用ObjectEvent,而不是使用成员函数,然后让它处理手动转换必要的:

using Function = std::function<void(Object*,Event*)>;

void handle(Object* o, Event* e) {
    ConcreteObject* co = dynamic_cast<ConcreteObject*>(o);
    ConcreteEvent* ce = dynamic_cast<ConcreteEvent*>(e);

    if (co and ce) { co->handle(ce); }
}

或者你喜欢的任何演员/支票。

注意:使用std::function与lambdas / functors兼容。