簡體   English   中英

向上轉換函數指針是否安全?

[英]Is it safe to upcast a function pointer?

我有基類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);

我保證調度程序將始終使用正確的事件進行調用,即,當它封裝的函數指針實際上采用SomeOtherConcreteEvent時,我不會調用調度程序並將其傳遞給ConcreteEvent

問題是:這是否有效? 在linux和mingw上,gcc 4.7當然可行。

從C ++ 11標准,第4.11.2節:

類型為“指向cv T類型的B的成員的指針”的prvalue,其中B是類類型,可以轉換為類型為“指向cv T類型的D的成員的指針”的prvalue,其中D是派生類( B.如果B是不可訪問的(第11條),不明確的(10.2)或D的虛擬(10.1)基類,或者是虛擬基類D的基類,則需要進行此轉換的程序是病態的。 轉換的結果引用與轉換發生前指向成員的指針相同的成員,但它引用基類成員,就好像它是派生類的成員一樣。

所以是的,這應該是安全的。

編輯:所以,如果你真的意味着向下轉換:這也是合法的,根據C ++ 11 5.2.9.12:

類型為“cv1 T類型D的成員的指針”類型的prvalue可以轉換為類型為cv2 T的“指向B成員的指針”的prvalue,其中B是D的基類(第10條),如果是從“指向T型成員B的指針”到“指向T型成員D的指針”的有效標准轉換存在(4.11),並且cv2與cv1具有相同的cv-quali fi cation,或更高的cv-quali fi cation。 69

我認為這並不安全,有兩個原因。

第一個是指向成員函數的指針只能向下安全傳播(因為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 ,你會發現它不能像你希望的那樣工作。


現在,這並不意味着您想要做的事情是不可能的,只是它需要更多

理想情況下,您只需修復簽名以獲取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兼容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM