web-dev-qa-db-ja.com

C ++での多重ディスパッチ

多重ディスパッチとは何かを理解しようとしています。いろいろな文章をたくさん読んでいますが、多重ディスパッチとは何か、何に役立つのかまだわかりません。たぶん私が見逃しているのは、多重ディスパッチを使用するコードの一部です。 C++には単一のディスパッチしかないため、正しくコンパイル/実行できないことがわかるように、複数のディスパッチを使用してC++で小さなコードを記述できますか?違いを確認する必要があります。ありがとう。

37
Martin

マルチディスパッチは、関数呼び出しに渡される引数の実行時型に基づいて、呼び出す関数のバージョンを選択する機能です。

これは、C++(未テスト)では正しく機能しない例です。

class A { };
class B : public A { };
class C : public A { }


class Foo
{
  virtual void MyFn(A* arg1, A* arg2) { printf("A,A\n"); }
  virtual void MyFn(B* arg1, B* arg2) { printf("B,B\n"); }
  virtual void MyFn(C* arg1, B* arg2) { printf("C,B\n"); }
  virtual void MyFn(B* arg1, C* arg2) { printf("B,C\n"); }
  virtual void MyFn(C* arg1, C* arg2) { printf("C,C\n"); }
};

void CallMyFn(A* arg1, A* arg2)
{
  // ideally, with multi-dispatch, at this point the correct MyFn() 
  // would be called, based on the RUNTIME type of arg1 and arg2
  pFoo->MyFn(arg1, arg2);
}

...

A* arg1 = new B();
A* arg2 = new C();
// Using multi-dispatch this would print "B,C"... but because C++ only
// uses single-dispatch it will print out "A,A"
CallMyFn(arg1, arg2);
67
Aaron

多重ディスパッチとは、実行される関数が複数のオブジェクトの実行時型に依存する場合です。

仮想関数を使用する場合、実行される実際の関数は->または。の左側にあるオブジェクトの実行時型にのみ依存するため、C++には単一のディスパッチがあります。オペレーター。

多重ディスパッチの実際のプログラミングケースを考えるのに苦労しています。たぶん、さまざまなキャラクターが互いに戦うゲームで。

void Fight(Opponent& opponent1, Opponent& opponent2);

戦いの勝者は両方の対戦相手の特性に依存する可能性があるため、両方の引数の実行時のタイプに応じて、この呼び出しを次のいずれかにディスパッチすることができます。

void Fight(Elephant& elephant, Mouse& mouse)
{
    mouse.Scare(elephant);
}

void Fight(Ninja& ninja, Mouse& mouse)
{
    ninja.KarateChop(mouse);
}

void Fight(Cat& cat, Mouse& mouse)
{
    cat.Catch(mouse);
}

void Fight(Ninja& ninja, Elephant& elephant)
{
    elephant.Trample(ninja);
}

// Etc.

関数の機能は、1つだけでなく、両方の引数のタイプによって異なります。 C++では、これをいくつかの仮想関数として記述しなければならない場合があります。仮想関数は、1つの引数(thisポインター)に応じて選択されます。次に、仮想関数には、他の引数に固有の処理を実行するためのスイッチなどが必要になる場合があります。

21
Scott Langham

B. Stroustrupによって書かれたこの論文を参照してください: Open Multi-Methods for C++

11

シングルディスパッチでは、実行される関数はオブジェクトタイプのみに依存します。 ダブルディスパッチでは、実行される関数はオブジェクトタイプとパラメータによって異なります。

次の例では、関数Area()はシングルディスパッチを使用して呼び出され、Intersect()はShapeパラメーターを受け取るため、ダブルディスパッチに依存しています。

class Circle;
class Rectangle;
class Shape
{
    virtual double Area() = 0; // Single dispatch

    // ...
    virtual double Intersect(const Shape& s) = 0; // double dispatch, take a Shape argument
    virtual double Intersect(const Circle& s) = 0; 
    virtual double Intersect(const Rectangle& s) = 0; 
};

struct Circle : public Shape
{
    virtual double Area() { return /* pi*r*r */; }

    virtual double Intersect(const Shape& s); 
    { return s.Intersect(*this)  ; }
    virtual double Intersect(const Circle& s); 
    { /*circle-circle*/ }
    virtual double Intersect(const Rectangle& s); 
    { /*circle-rectangle*/ }
};

例はこれに基づいています 記事

3
Phillip Ngan