3

我有基类Object和一个Event

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 中肯定可以正常工作。

4

2 回答 2

4

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

“指向类型为 cv T 的 B 的成员的指针”类型的纯右值,其中 B 是类类型,可以转换为类型为“指向类型为 cv T 的 D 的成员的指针”类型的纯右值,其中 D 是派生类 ( B 的第 10 条)。如果 B 是 D 的不可访问(第 11 条)、模棱两可(10.2)或虚拟(10.1)基类,或 D 的虚拟基类的基类,则需要进行此转换的程序是格式不正确。转换的结果与发生转换之前的成员指针引用相同的成员,但它引用基类成员,就好像它是派生类的成员一样。

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

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

“指向 cv1 T 的成员的指针”类型的纯右值可以转换为 cv2 T 类型的“指向 B 的成员的指针”类型的纯右值,其中 B 是 D 的基类(第 10 条),如果存在从“指向 T 类型 B 成员的指针”到“指向 T 类型 D 成员的指针”的有效标准转换 (4.11),并且 cv2 与 cv1 具有相同的 cv 限定或大于 cv1 的 cv 限定。69

于 2013-02-19T15:52:40.550 回答
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;
}

(如此处所示

第二个是你不能像那样改变参数的类型。为了说明这个问题,使用ConcreteObject: public virtual Object它,你会发现它并没有像你希望的那样工作。


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

理想情况下,您只需将签名修复Object为同时采用 an和 an Event,而不是使用成员函数,然后在必要时让它处理手动转换:

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 兼容。

于 2013-02-19T16:14:28.230 回答