I have an abstract type A, and two derived types A1 and A2.
I want to add a method M to class A, which takes a parameter of type A. But, I need ad hoc polymorphism.
Indeed, I need 3 implementations : A1::M(A1 a), A1::M(A2 a) and A2::(A1 a), A2::M(A2 a).
But I want an abstract way to call the method M with pointers of type A.
I could put all signatures declaration in class A, but it sucks.
This is double dispatch. When you write:
should dispatch both on the type of
*p1and the type of*p2.Before starting, you must realize that this means
n^2functions forndifferent derived types. And that somewhere, someone must be awareof all of the derived types (unless you can define some sort of
“default” implemenation for an unknown pair of types).
There are two ways of implementing this. The simplest, if the hierarchy
is closed (i.e. client code cannot introduce new derived classes) does
use a host of virtual functions in the base class—generally
protected, since they’re not designed to be called outside the
hierarchy:
In the derived classes, the implementation of
Mis always the same:This is simple, and relatively efficient, but requires changes in the
abstract base and all of the derived classes (which have to implement
the new virtual function) each time you add a new derived class. It’s
useful for closed hierarchies, however; I’ve used it in classes using
the strategy pattern for part of their behavior, where the various
strategies were all defined in the source file, and not exposed to the
clients (and the abstract base of the strategies was only forward
declared in the header, so no header changes were necessary if I added a
strategy).
A more general solution would involve an
std::map, with a pair oftypeidas index. You can’t usetypeiddirectly, since it’s notcopyable. C++11 provides a
type_indexto wrap it; if you’re using anolder compiler, it’s fairly trivial to implement one yourself. The
basic principle is something along the lines of (probably in A itself):
with:
The real problem comes with writing each of the individual functions and
inserting their addresses in the map (before first use). The functions
themselves, of course, can use
dynamic_cast, or evenstatic_cast, ifyou can be sure that they will only be called from here, and they can be
friend(s) of the class(es) involved, but there are still
n2 of them. (One frequent solution is to make them
static members of one of the classes, and to have each derived class
define a static member of a type which does the registration of all of
the functions its responsible for.)