必要なのは、最初の引数だけでなく、2 番目の引数でもディスパッチする関数、つまり二重ディスパッチです。これは C++ では直接サポートされていませんが、エミュレートできます。
class Point;
class Line;
class Collider {
public:
virtual bool Collides(Collider const& x) const = 0;
protected:
virtual bool Collides(Point const& p) const = 0;
virtual bool Collides(Line const& l) const = 0;
friend class Point;
friend class Line;
};
class Point: public Collider {
public:
virtual bool Collides(Collider const& x) const {
// here, the type of this is now Point
// now, find out what x is and call the overload on this type
return x.Collides(*this);
}
protected:
virtual bool Collides(Point const& p) const {
return false;
// we now know that we are a point and checking vs. another point
}
virtual bool Collides(Line const& l) const {
return false;
// we now know that we are a point and checking vs. a line
}
};
class Line: public Collider {
public:
virtual bool Collides(Collider const& x) const {
// here, the type of this is now Line
return x.Collides(*this);
}
protected:
virtual bool Collides(Point const& p) const {
return false;
// we now know that we are a line and checking vs. a point
}
virtual bool Collides(Line const& l) const {
return false;
// we now know that we are a line and checking vs. another line
}
};
現在、2 つのオブジェクトをチェックすると、2 つのランタイム ディスパッチが行われます。
Collider* p = new Point();
Collider* l = new Line();
p->Collides(*l);
// first dynamically dispatches on p to call Point::Collides
// in Collides, this (which is p) now has the static Point.
// now do the second dispatch on l and use *this as the parametet
// to find the overload.
これは、たとえばVisitor デザイン パターンで使用されます。オブジェクトのセットが固定されているが、それらに対して実行される操作が変更または拡張されることが予想される場合は、Visitor が適切な選択です。