Double dispatch

From Wikipedia, the free encyclopedia
Jump to: navigation, search

In software engineering, double dispatch is a special form of multiple dispatch, and a mechanism that dispatches a function call to different concrete functions depending on the runtime types of two objects involved in the call. In most object-oriented systems, the concrete function that is called from a function call in the code depends on the dynamic type of a single object and therefore they are known as single dispatch calls, or simply virtual function calls.

Contents

Examples [edit]

Double dispatch is useful in situations where the choice of computation depends on the runtime types of its arguments. For example, a programmer could use double dispatch in the following situations:

  • Adaptive collision algorithms usually require that collisions between different objects be handled in different ways. A typical example is in a game environment where the collision between a spaceship and an asteroid is computed differently than the collision between a spaceship and a spacestation.[1]
  • Painting algorithms that require the intersection points of overlapping sprites be rendered in a different manner.
  • Personnel management systems may dispatch different types of jobs to different personnel. A schedule algorithm that is given a person object typed as an accountant and a job object typed as engineering rejects the scheduling of that person for that job.
  • Event handling systems that use both the event type and the type of the receptor object in order to call the correct event handling routine.

A common idiom [edit]

The common idiom in the examples presented above, is that the selection of the appropriate algorithm is based on the call's argument types at runtime. The call is therefore subject to all the usual additional performance costs that are associated with dynamic resolution of calls, usually more than in a language supporting only single method dispatch. In C++, for example, a dynamic function call is usually resolved by a single offset calculation - which is possible because the compiler knows the location of the function in the object's method table and so can statically calculate the offset. In a language supporting double dispatch, this is slightly more costly, because the compiler must generate code to calculate the method's offset in the method table at runtime, thereby increasing the overall instruction path length (by an amount that is likely to be no more than the total number of calls to the function, which may not be very significant).

Double dispatch is more than function overloading [edit]

At first glance, double dispatch appears to be a natural result of function overloading. Function overloading allows the function called to depend on the type of the argument. Function overloading however is done at compile time using "name mangling" where the internal name of the function has the argument's type encoded in it. So for example a function foo(int) would internally be called __foo_i and function foo(double) would be called __foo_d. So there is no runtime overhead because there is no name collision and calling an overloaded function goes through at most one virtual table just like any other function. Dynamic dispatch is only based on the type of the calling object. Consider the following example, written in C++, of collisions in a game:

class SpaceShip {};
class ApolloSpacecraft : public SpaceShip {};
 
class Asteroid {
public:
  virtual void CollideWith(SpaceShip&) {
    cout << "Asteroid hit a SpaceShip" << endl;
  }
  virtual void CollideWith(ApolloSpacecraft&) {
    cout << "Asteroid hit an ApolloSpacecraft" << endl;
  }
};
 
class ExplodingAsteroid : public Asteroid {
public:
  virtual void CollideWith(SpaceShip&) {
    cout << "ExplodingAsteroid hit a SpaceShip" << endl;
  }
  virtual void CollideWith(ApolloSpacecraft&) {
    cout << "ExplodingAsteroid hit an ApolloSpacecraft" << endl;
  }
};

If you have

Asteroid theAsteroid;
SpaceShip theSpaceShip;
ApolloSpacecraft theApolloSpacecraft;

then, because of function overloading,

theAsteroid.CollideWith(theSpaceShip); 
theAsteroid.CollideWith(theApolloSpacecraft);

will print Asteroid hit a SpaceShip and Asteroid hit an ApolloSpacecraft respectively, without using any dynamic dispatch. Furthermore

ExplodingAsteroid theExplodingAsteroid;
theExplodingAsteroid.CollideWith(theSpaceShip); 
theExplodingAsteroid.CollideWith(theApolloSpacecraft);

will print ExplodingAsteroid hit a SpaceShip and ExplodingAsteroid hit an ApolloSpacecraft respectively, again without dynamic dispatch.

With a reference to an Asteroid, dynamic dispatch is used and

Asteroid& theAsteroidReference = theExplodingAsteroid;
theAsteroidReference.CollideWith(theSpaceShip); 
theAsteroidReference.CollideWith(theApolloSpacecraft);

prints ExplodingAsteroid hit a SpaceShip and ExplodingAsteroid hit an ApolloSpacecraft, again as expected. However,

SpaceShip& theSpaceShipReference = theApolloSpacecraft;
//note the type of the pointer and the type of the object.
theAsteroid.CollideWith(theSpaceShipReference);
theAsteroidReference.CollideWith(theSpaceShipReference);

The desired behaviour is to bind these calls to the function that takes theApolloSpacecraft as its argument, as that is the instantiated type of the variable, meaning the expected output would be Asteroid hit an ApolloSpacecraft and ExplodingAsteroid hit an ApolloSpacecraft, but the output is actually Asteroid hit a SpaceShip and ExplodingAsteroid hit a SpaceShip. The problem is that, while virtual functions are dispatched dynamically in C++, function overloading is done statically.

Double dispatch in C++ [edit]

The problem described above can be resolved by simulating double dispatch, for example by using a visitor pattern. Suppose SpaceShip and ApolloSpacecraft both have the function

virtual void CollideWith(Asteroid& inAsteroid) {
  inAsteroid.CollideWith(*this);
}

Then, while the previous example still does not work correctly, the following does:

SpaceShip& theSpaceShipReference = theApolloSpacecraft;
Asteroid& theAsteroidReference = theExplodingAsteroid;
theSpaceShipReference.CollideWith(theAsteroid);
theSpaceShipReference.CollideWith(theAsteroidReference);

It prints out Asteroid hit an ApolloSpacecraft and ExplodingAsteroid hit an ApolloSpacecraft, as expected. The key is that theSpaceShipReference.CollideWith(theAsteroidReference); does the following at run time:

  1. theSpaceShipReference is a reference, so C++ looks up the correct method in the vtable. In this case, it will call ApolloSpacecraft::CollideWith(Asteroid&).
  2. Within ApolloSpacecraft::CollideWith(Asteroid&), inAsteroid is a reference, so inAsteroid.CollideWith(*this) will result in another vtable lookup. In this case, inAsteroid is a reference to an ExplodingAsteroid so ExplodingAsteroid::CollideWith(ApolloSpacecraft&) will be called.

Double dispatch in Eiffel [edit]

The Eiffel programming language can bring the concept of agents to bear on the double-dispatch problem. The example below applies the agent language construct to the double-dispatch problem.

Consider a problem domain with various forms of SHAPE and of drawing SURFACE upon which to draw a SHAPE. Both SHAPE and SURFACE know about a function called `draw' in themselves, but not in each other. We want objects of the two types to co-variantly interact with each other in a double-dispatch using a visitor pattern.

The challenge is to get a polymorphic SURFACE to draw a polymorphic SHAPE on itself.

Output [edit]

The output example below shows the results of two SURFACE visitor objects being polymorphically passed over a list of polymorphic SHAPE objects. The visitor code pattern is only aware of SHAPE and SURFACE generically and not of the specific type of either. Instead, the code relies on run-time polymorphism and the mechanics of agents to achieve a highly flexible co-variant relationship between these two deferred classes and their descendants.

draw a red POLYGON on ETCHASKETCH
draw a red POLYGON on GRAFFITI_WALL
draw a grey RECTANGLE on ETCHASKETCH
draw a grey RECTANGLE on GRAFFITI_WALL
draw a green QUADRILATERAL on ETCHASKETCH
draw a green QUADRILATERAL on GRAFFITI_WALL
draw a blue PARALLELOGRAM on ETCHASKETCH
draw a blue PARALLELOGRAM on GRAFFITI_WALL
draw a yellow POLYGON on ETCHASKETCH
draw a yellow POLYGON on GRAFFITI_WALL
draw a purple RECTANGLE on ETCHASKETCH
draw a purple RECTANGLE on GRAFFITI_WALL

Press Return to finish the execution...

Setup [edit]

Before looking at SHAPE or SURFACE, we need to examine the high level decoupled use of our double-dispatch.

Visitor Pattern [edit]

The visitor pattern is always a visitor object visiting the elements of a data structure (e.g. list, tree and so on) polymorphically, applying some action (call or agent) against the polymorphic element objects in the visited target structure.

In our example below, we make a list of polymorphic SHAPE objects, visiting each of them with a polymorphic SURFACE, asking the SHAPE to be drawn on the SURFACE.

1       make
2                       -- Print shapes on surfaces.
3               local
4                       l_shapes: ARRAYED_LIST [SHAPE]
5                       l_surfaces: ARRAYED_LIST [SURFACE]
6               do
7                       create l_shapes.make (6)
8                       l_shapes.extend (create {POLYGON}.make_with_color ("red"))
9                       l_shapes.extend (create {RECTANGLE}.make_with_color ("grey"))
10                      l_shapes.extend (create {QUADRILATERAL}.make_with_color ("green"))
11                      l_shapes.extend (create {PARALLELOGRAM}.make_with_color ("blue"))
12                      l_shapes.extend (create {POLYGON}.make_with_color ("yellow"))
13                      l_shapes.extend (create {RECTANGLE}.make_with_color ("purple"))
14
15                      create l_surfaces.make (2)
16                      l_surfaces.extend (create {ETCHASKETCH}.make)
17                      l_surfaces.extend (create {GRAFFITI_WALL}.make)
18
19                      across l_shapes as ic_shapes loop
20                              across l_surfaces as ic_surfaces loop
21                                      check attached {SURFACE} ic_surfaces.item as al_surface then
22                                              check attached {SHAPE} ic_shapes.item as al_shape then
23                                                      al_surface.drawing_agent.call ([al_shape.drawing_data_agent])
24                                              end
25                                      end
26                              end
27                      end
28              end

We start by creating a collection of SHAPE and SURFACE objects. We then iterate over one of the lists (SHAPE), allowing elements of the other (SURFACE) to visit each of them in turn. In the example code above, SURFACE objects are visiting SHAPE objects.

The code makes a polymorphic call on {SURFACE}.draw indirectly by way of the `drawing_agent', which is the first call (dispatch) of the double-dispatch pattern. It passes an indirect and polymorphic agent (`drawing_data_agent'), allowing our visitor code to only know about two things:

  • What is the drawing agent of the surface (e.g. al_surface.drawing_agent on line #23)?
  • What is the drawing data agent of the shape (e.g. al_shape.drawing_data_agent on line #23)?

Lines #21 and #22 only codify our contractual expectation in an attachment check for both SURFACE and SHAPE. This ensures we're working only with fully conforming data types on our co-variant objects.

Because both SURFACE and SHAPE define their own agents, our visitor code is freed from having to know what is the appropriate call to make, polymorphically or otherwise. This level of indirection and decoupling is simply not achievable in other common languages like C, C++ and Java except through either some form of reflection or feature overloading with signature matching.

SURFACE [edit]

Within the polymorphic call to {SURFACE}.draw is the call to an agent, which becomes the second polymorphic call or dispatch in the double-dispatch pattern.

1       deferred class
2               SURFACE
3       
4       feature {NONE} -- Initialization
5       
6               make
7                               -- Initialize Current.
8                       do
9                               default_create
10                              drawing_agent := agent draw
11                      end
12      
13      feature -- Access
14
15              drawing_agent: PROCEDURE [ANY, TUPLE]
16                              -- Drawing agent of Current.
17      
18      feature {NONE} -- Implementation
19      
20              draw (a_data_agent: FUNCTION [ANY, TUPLE, TUPLE [name, color: STRING]])
21                              -- Draw `a_shape' on Current.
22                      do
23                              a_data_agent.call ([Void])
24                              check attached {TUPLE [name, color: STRING]} a_data_agent.last_result as al_data then
25                                      print ("draw a " + al_data.color + " " + al_data.name + " on " + type + "%N")
26                              end
27                      end
28      
29              type: STRING
30                              -- Type name of Current.
31                      deferred
32                      end
33      
34      end

The agent argument in line #20 and call in line #23 are both polymorphic and decoupled. The agent is decoupled because the {SURFACE}.draw feature has no idea what class `a_data_agent' is based on. There is no way to tell what class the operation agent was derived from, so it does not have to come from SHAPE or one of its descendants. This is a distinct advantage of Eiffel agents over the single inheritance, dynamic and polymorphic binding of other languages.

The agent is dynamically polymorphic at run-time because the object is created in the moment it is needed, dynamically, where the version of the objectified routine is determined at that time. The only strongly bound knowledge is of the Result type of the agent signature -- that is -- a named TUPLE with two elements. However, this specific requirement is based on a demand of the enclosing feature (e.g. line #25 uses the named elements of the TUPLE to fulfill `draw' feature of SURFACE), which is necessary and has not been avoided (and perhaps cannot be).

Finally, note how only the `drawing_agent' feature is exported to ANY client! This means that the visitor pattern code (who is the ONLY client of this class) only needs to know about the agent to get its job done (e.g. using the agent as the feature applied to the visited objects).

SHAPE [edit]

The SHAPE class has the basis (e.g. drawing data) for what is drawn, perhaps on a SURFACE, but it does not have to be. Again, the agents provide the indirection and class agnostics required to make the co-variant relationship with SHAPE as decoupled as possible.

Additionally, please take note of the fact that SHAPE only provides `drawing_data_agent' as a fully exported feature to any client. Therefore, the only way to interact with SHAPE, other than creation, is through the facilities of the `drawing_data_agent', which is used by ANY client to indirectly and polymorphically gather drawing data for the SHAPE!

1       deferred class
2               SHAPE
3       
4       feature {NONE} -- Initialization
5       
6               make_with_color (a_color: like color)
7                               -- Initialize Current with `a_color'.
8                       do
9                               color := a_color
10                              drawing_data_agent := agent drawing_data
11                      ensure
12                              color_set: color.same_string (a_color)
13                      end
14
15      feature -- Access
16      
17              drawing_data_agent: FUNCTION [ANY, TUPLE, attached like drawing_data_anchor]
18                              -- agent al_shape.drawing_data
19      
20      feature {NONE} -- Implementation
21      
22      
23              drawing_data: attached like drawing_data_anchor
24                              -- Data needed for drawing of Current.
25                      do
26                              Result := [name, color]
27                      end
28      
29              name: STRING
30                              -- Object name of Current.
31                      deferred
32                      end
33      
34              color: STRING
35                              -- Color of Current.
36      
37              drawing_data_anchor: detachable TUPLE [name: like name; color: like color]
38                              -- Type anchor for `drawing_data' and others.
39
40      end

Classic Spaceship Example [edit]

A variation of the classic Spaceship example has one or more spaceship objects roaming about a universe filled with other items like rogue asteroids and space stations. What we want is a double-dispatch method for handling encounters (e.g. possible collisions) between two co-variant objects in our make-believe universe.

In our example below, the output excursion of our USS Enterprise and USS Excelsior will be:

Starship Enterprise changes position from A-001 to A-002.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 1'!
Starship Enterprise changes position from A-002 to A-003.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 2'!
Starship Enterprise beams a science team to Starship Excelsior as they pass!
Starship Enterprise changes position from A-003 to A-004.
Starship Excelsior changes position from A-003 to A-005.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 3'!
Starship Excelsior is near Space Station Deep Space 9 and is dockable.
Starship Enterprise changes position from A-004 to A-005.
Starship Enterprise beams a science team to Starship Excelsior as they pass!
Starship Enterprise is near Space Station Deep Space 9 and is dockable.

Visitor [edit]

The visitor for the classic Spaceship example also has a double-dispatch mechanism.

1               make
2                               -- Allow SPACESHIP objects to visit and move about in a universe.
3                       local
4                               l_universe: ARRAYED_LIST [SPACE_OBJECT]
5                               l_enterprise,
6                               l_excelsior: SPACESHIP
7                       do
8                               create l_enterprise.make_with_name ("Enterprise", "A-001")
9                               create l_excelsior.make_with_name ("Excelsior", "A-003")
10      
11                              create l_universe.make (0)
12                              l_universe.force (l_enterprise)
13                              l_universe.force (create {ASTEROID}.make_with_name ("Rogue 1", "A-002"))
14                              l_universe.force (create {ASTEROID}.make_with_name ("Rogue 2", "A-003"))
15                              l_universe.force (l_excelsior)
16                              l_universe.force (create {ASTEROID}.make_with_name ("Rogue 3", "A-004"))
17                              l_universe.force (create {SPACESTATION}.make_with_name ("Deep Space 9", "A-005"))
18      
19                              visit (l_enterprise, l_universe)
20                              l_enterprise.set_position ("A-002")
21                              visit (l_enterprise, l_universe)
22                              l_enterprise.set_position ("A-003")
23                              visit (l_enterprise, l_universe)
24                              l_enterprise.set_position ("A-004")
25                              l_excelsior.set_position ("A-005")
26                              visit (l_enterprise, l_universe)
27                              visit (l_excelsior, l_universe)
28                              l_enterprise.set_position ("A-005")
29                              visit (l_enterprise, l_universe)
30                      end
31      
32      feature {NONE} -- Implementation
33      
34              visit (a_object: SPACE_OBJECT; a_universe: ARRAYED_LIST [SPACE_OBJECT])
35                              -- `a_object' visits `a_universe'.
36                      do
37                              across a_universe as ic_universe loop
38                                      check attached {SPACE_OBJECT} ic_universe.item as al_universe_object then
39                                              a_object.encounter_agent.call ([al_universe_object.sensor_data_agent])
40                                      end
41                              end
42                      end

The double-dispatch can be see in line #39, where two indirect agents are working together to provide two co-variant calls working in perfect polymorphic concert with each other. The `a_object' of the `visit' feature has an `encounter_agent' which is called with the sensor data of the `sensor_data_agent' coming from the `al_universe_object'.

The other interesting part of this particular example is the SPACE_OBJECT class and its `encounter' feature:

Visitor Action [edit]

The only exported features of a SPACE_OBJECT are the agents for encounter and sensor data, as well as the capacity to set a new position. As one object (the spaceship) visits each object in the universe, the sensor data is collected and passed to the visiting object in its encounter agent. There, the sensor data from the sensor_data_agent (that is -- the data element items of the sensor_data TUPLE as returned by the sensor_data_agent query) are evaluated against the current object and a course of action is taken based on that evaluation (see `encounter' in SPACE_OBJECT below).

All other data is exported to {NONE}. This is similar to C, C++ and Java scopes of Private. As non-exported features, the data and routines are used only internally by each SPACE_OBJECT.

Finally, note that encounter calls to `print' do not include specific information about possible descendant classes of SPACE_OBJECT! The only thing found at this level in the inheritance are general relational aspects based completely on what can be known from the attributes and routines of a general SPACE_OBJECT. The fact that the output of the `print' makes sense to us, as human beings, based on what we know or imagine about Star ships, space stations and asteroids is merely logical planning or coincidence. The SPACE_OBJECT is not programmed with any specific knowledge of it descendants.

1       deferred class
2               SPACE_OBJECT
3       
4       feature {NONE} -- Initialization
5       
6               make_with_name (a_name: like name; a_position: like position)
7                               -- Initialize Current with `a_name' and `a_position'.
8                       do
9                               name := a_name
10                              position := a_position
11                              sensor_data_agent := agent sensor_data
12                              encounter_agent := agent encounter
13                      ensure
14                              name_set: name.same_string (a_name)
15                              position_set: position.same_string (a_position)
16                      end
17      
18      feature -- Access
19      
20              encounter_agent: PROCEDURE [ANY, TUPLE]
21                              -- Agent for managing encounters with Current.
22      
23              sensor_data_agent: FUNCTION [ANY, TUPLE, attached like sensor_data_anchor]
24                              -- Agent for returning sensor data of Current.
25      
26      feature -- Settings
27      
28              set_position (a_position: like position)
29                              -- Set `position' with `a_position'.
30                      do
31                              print (type + " " + name + " changes position from " + position + " to " + a_position + ".%N")
32                              position := a_position
33                      ensure
34                              position_set: position.same_string (a_position)
35                      end
36      
37      feature {NONE} -- Implementation
38      
39              encounter (a_sensor_agent: FUNCTION [ANY, TUPLE, attached like sensor_data_anchor])
40                              -- Detect and report on collision status of Current with `a_radar_agent'.
41                      do
42                              a_sensor_agent.call ([Void])
43                              check attached {like sensor_data_anchor} a_sensor_agent.last_result as al_sensor_data then
44                                      if not name.same_string (al_sensor_data.name) then
45      
46                                              if (position.same_string (al_sensor_data.position)) then
47                                                      if ((al_sensor_data.is_dockable and is_dockable) and
48                                                                      (is_manned and al_sensor_data.is_manned) and
49                                                                      (is_manueverable and al_sensor_data.is_not_manueverable)) then
50      
51                                                              print (type + " " + name + " is near " + al_sensor_data.type + " " +
52                                                                              al_sensor_data.name + " and is dockable.%N")
53      
54                                                      elseif ((is_dockable and al_sensor_data.is_dockable) and
55                                                                              (is_manned and al_sensor_data.is_manned) and
56                                                                              (is_manueverable and al_sensor_data.is_manueverable)) then
57      
58                                                              print (type + " " + name + " beams a science team to " + al_sensor_data.type + " " +
59                                                                              al_sensor_data.name + " as they pass!%N")
60      
61                                                      elseif (is_manned and al_sensor_data.is_not_manned) then
62                                                              print (type + " " + name + " takes evasive action, avoiding " +
63                                                                              al_sensor_data.type + " `" + al_sensor_data.name + "'!%N")
64                                                      end
65                                              end
66      
67                                      end
68                              end
69                      end
70      
71              name: STRING
72                              -- Name of Current.
73      
74              type: STRING
75                              -- Type of Current.
76                      deferred
77                      end
78      
79              position: STRING
80                              -- Position of Current.
81      
82              is_dockable: BOOLEAN
83                              -- Is Current dockable with another manned object?
84                      deferred
85                      end
86      
87              is_manned: BOOLEAN
88                              -- Is Current a manned object?
89                      deferred
90                      end
91      
92              is_manueverable: BOOLEAN
93                              -- Is Current capable of being moved?
94                      deferred
95                      end
96      
97              sensor_data: attached like sensor_data_anchor
98                              -- Sensor data of Current.
99                      do
100                             Result := [name, type, position, is_dockable, not is_dockable, is_manned, not is_manned, is_manueverable, not is_manueverable]
101                     end
102     
103             sensor_data_anchor: detachable TUPLE [name, type, position: STRING; is_dockable, is_not_dockable, is_manned, is_not_manned, is_manueverable, is_not_manueverable: BOOLEAN]
104                             -- Sensor data type anchor of Current.
105     
106     end

There are three descendant classes of SPACE_OBJECT:

SPACE_OBJECT
        ASTEROID
        SPACESHIP
        SPACESTATION

In our example, the ASTEROID class is used for the `Rogue' items, SPACESHIP for the two star ships and SPACESTATION for Deep Space Nine. In each class, the only specialization is the setting of the `type' feature and of certain properties of the object. The `name' is supplied in the creation routine as well as the `position'.

For example: Below is the SPACESHIP example.

class
        SPACESHIP

inherit
        SPACE_OBJECT

create
        make_with_name

feature {NONE} -- Implementation

        type: STRING = "Starship"
                        -- <Precursor>

        is_dockable: BOOLEAN = True
                        -- <Precursor>

        is_manned: BOOLEAN = True
                        -- <Precursor>

        is_manueverable: BOOLEAN = True
                        -- <Precursor>

end

So, any SPACESHIP in our universe is dock-able, manned and maneuverable. Other objects, like Asteroids are none of these things. A SPACESTATION, on the other hand, is both dock-able and manned, but is not maneuverable. Thus, when one object has an encounter with another, it first checks to see if the positions put them in the vicinity of each other and if they are, then the objects interact based upon their basic properties.

Note that objects with the same type and name are considered to the same object, so an interaction is logically disallowed.

Eiffel Example Conclusion [edit]

With regards to double-dispatch, Eiffel allows the designer and programmer to further remove a level of direct object-to-object knowledge by decoupling class routines from their classes by way of making them agents and then passing those agents instead of making direct object feature calls. The agents also have specific signatures and possible results (in the case of queries), making them ideal static type checking vehicles without giving up specific object details. The agents are fully polymorphic so that the resulting code has only the specific knowledge required to get its local job done. Otherwise, there is no maintenance burden added by having specific internal class feature knowledge spread around many co-variant objects. The use and mechanics agents ensure this.

One possible downside of the use of agents is that an agent is computationally more expensive than its direct call counterpart. With this in mind, one ought never to presume the use of agents in the double-dispatch and their application in visitor patterns. If one can clearly see a design limit as to the domain of class types that will be involved in the co-variant interactions, then a direct call is the more efficient solution in terms of computational expense. However, if the class domain of participating types is expected to grow or differ substantially, then agents present an excellent solution to lessening the maintenance burden in the double-dispatch pattern.

See also [edit]

References [edit]

  1. ^ More Effective C++ by Scott Meyers(Addison-Wesley, 1996)

External links [edit]