3

私は基本クラスObjectEvent

class Object
{
//...
};

class Event
{
};

そして、関数ポインタの typedef

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

そして、2 つのポインターを格納する無関係なクラス

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 のメンバーへのポインター」の prvalue は、タイプ「タイプ 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 は、タイプ cv2 T のタイプ「B のメンバーへのポインター」の prvalue に変換できます。ここで、B は D の基本クラス (条項 10) です。 「T 型の B のメンバへのポインタ」から「T 型の D のメンバへのポインタ」への有効な標準変換が存在し (4.11)、cv2 が cv1 と同じ cv 修飾であるか、cv 修飾より大きい。69

于 2013-02-19T15:52:40.550 に答える
1

2つの理由から、安全ではないと主張します。

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

(ここに見られるように)

2 つ目は、そのように引数の型を変更できないことです。問題を説明するために、 を使用する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ラムダ/ファンクターとの互換性のために使用します。

于 2013-02-19T16:14:28.230 に答える