"Virtual functions allow polymorphism on a single argument" ?

Discussion in 'C++' started by desktop, May 10, 2007.

  1. desktop

    desktop Guest

    This page:

    http://www.eptacom.net/pubblicazioni/pub_eng/mdisp.html

    start with the line: "Virtual functions allow polymorphism on a single
    argument". What does that exactly mean?

    I guess it has nothing to do with making multiple arguments in a
    declaration like:

    virtual void setId(int a, int b) {id = a+b;}
     
    desktop, May 10, 2007
    #1
    1. Advertising

  2. * desktop:
    > This page:
    >
    > http://www.eptacom.net/pubblicazioni/pub_eng/mdisp.html
    >
    > start with the line: "Virtual functions allow polymorphism on a single
    > argument". What does that exactly mean?
    >
    > I guess it has nothing to do with making multiple arguments in a
    > declaration like:
    >
    > virtual void setId(int a, int b) {id = a+b;}


    Right. The argument in question is the implicit this-pointer, the
    object you're calling the member function on. And what it means is that
    what member function implementation to call is selected based on the run
    time type of that argument.

    Polymorphism on two or more arguments is difficult because the number of
    possible function implementations is then the product of the number of
    possible classes for each argument.

    One useful technique is known as double dispatch; look it up.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, May 10, 2007
    #2
    1. Advertising

  3. desktop wrote:
    > This page:
    >
    > http://www.eptacom.net/pubblicazioni/pub_eng/mdisp.html
    >
    > start with the line: "Virtual functions allow polymorphism on a single
    > argument". What does that exactly mean?
    >
    > I guess it has nothing to do with making multiple arguments in a
    > declaration like:
    >
    > virtual void setId(int a, int b) {id = a+b;}


    Sort of. The "single argument" relates to the polymorphism based on the
    object itself -- difference in behaviours depending on the dynamic (or
    the creation) class of the object. This is (if you read further) put
    in opposition to "multi-argument polymorphism" where the behaviour of
    a pair of objects should be specific, IOW, along with the object itself
    you need to involve the actual explicit argument in defining the
    behaviour.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, May 10, 2007
    #3
  4. desktop

    desktop Guest

    Alf P. Steinbach wrote:
    > * desktop:
    >> This page:
    >>
    >> http://www.eptacom.net/pubblicazioni/pub_eng/mdisp.html
    >>
    >> start with the line: "Virtual functions allow polymorphism on a single
    >> argument". What does that exactly mean?
    >>
    >> I guess it has nothing to do with making multiple arguments in a
    >> declaration like:
    >>
    >> virtual void setId(int a, int b) {id = a+b;}

    >
    > Right. The argument in question is the implicit this-pointer, the
    > object you're calling the member function on. And what it means is that
    > what member function implementation to call is selected based on the run
    > time type of that argument.


    Ok so the argument in question is "obj" in this context:

    obj.callMe()

    where obj is the object that the member function "callMe()" is called upon.

    Since obj can be an instance of B,C or D (if they are all descendants
    from a base class A) it is first at runtime it is decided which (B, C or
    D) "callme()" function will be run.


    > Polymorphism on two or more arguments is difficult because the number of
    > possible function implementations is then the product of the number of
    > possible classes for each argument.



    But how can there be more than one object that a function is called
    upon? As I see it there can only be one (like obj) but it might differ
    at runtime which type it is.


    > One useful technique is known as double dispatch; look it up.


    I am currently reading this pattern but need to understand what they
    mean with polymorphism with one or two arguments.
     
    desktop, May 10, 2007
    #4
  5. desktop

    Rolf Magnus Guest

    desktop wrote:

    > Alf P. Steinbach wrote:
    >> * desktop:
    >>> This page:
    >>>
    >>> http://www.eptacom.net/pubblicazioni/pub_eng/mdisp.html
    >>>
    >>> start with the line: "Virtual functions allow polymorphism on a single
    >>> argument". What does that exactly mean?
    >>>
    >>> I guess it has nothing to do with making multiple arguments in a
    >>> declaration like:
    >>>
    >>> virtual void setId(int a, int b) {id = a+b;}

    >>
    >> Right. The argument in question is the implicit this-pointer, the
    >> object you're calling the member function on. And what it means is that
    >> what member function implementation to call is selected based on the run
    >> time type of that argument.

    >
    > Ok so the argument in question is "obj" in this context:
    >
    > obj.callMe()
    >
    > where obj is the object that the member function "callMe()" is called
    > upon.


    obj must be a reference, otherwise there is no polymorphism.

    > Since obj can be an instance of B,C or D (if they are all descendants
    > from a base class A) it is first at runtime it is decided which (B, C or
    > D) "callme()" function will be run.
    >
    >
    >> Polymorphism on two or more arguments is difficult because the number of
    >> possible function implementations is then the product of the number of
    >> possible classes for each argument.

    >
    >
    > But how can there be more than one object that a function is called
    > upon? As I see it there can only be one (like obj) but it might differ
    > at runtime which type it is.



    Right, and that's exactly the reason why this is "polymorphism on a single
    argument".

    >> One useful technique is known as double dispatch; look it up.

    >
    > I am currently reading this pattern but need to understand what they
    > mean with polymorphism with one or two arguments.


    It simply means that the function that is seleccted at runtime depends on
    the dynamic type of one or two objects.
     
    Rolf Magnus, May 11, 2007
    #5
  6. desktop

    James Kanze Guest

    On May 11, 12:07 am, desktop <> wrote:
    > Alf P. Steinbach wrote:
    > > * desktop:
    > >> This page:


    > >>http://www.eptacom.net/pubblicazioni/pub_eng/mdisp.html


    > >> start with the line: "Virtual functions allow polymorphism on a single
    > >> argument". What does that exactly mean?


    > >> I guess it has nothing to do with making multiple arguments in a
    > >> declaration like:


    > >> virtual void setId(int a, int b) {id = a+b;}


    > > Right. The argument in question is the implicit this-pointer, the
    > > object you're calling the member function on. And what it means is that
    > > what member function implementation to call is selected based on the run
    > > time type of that argument.


    > Ok so the argument in question is "obj" in this context:


    > obj.callMe()


    > where obj is the object that the member function "callMe()" is called upon.


    Yes. In C++ syntax. Conceptually, this can be mapped to
    callMe( obj ), with polymorphism always occuring on the first
    object (in C++).

    Now consider something like "obj.callMe( arg )". Conceptually,
    this would be "callMe( obj, arg )". In C++, polymorphism only
    works on the first argument here. In other languages, the
    actual function called can depend on the dynamic type of both
    arguments, e.g. in CLOS: "(callMe obj arg)", the actual function
    called can depend on the type of obj, the type of arg or both
    (or neither).

    As Alf pointed out, this can get a bit hairy: if both arguments
    can have 10 different types, this means 100 different functions.
    Which you have to write. And to add a new type, you practically
    have to know all of the existing types, in order to add all of
    the additional functions.

    In practice, in C++, this can be implemented by calling a
    virtual function on the first object, and having it call a
    virtual function on the second. But with the constraint that
    the base class has to know of the existance of all of the
    derived types.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 11, 2007
    #6
  7. desktop

    desktop Guest

    James Kanze wrote:
    > On May 11, 12:07 am, desktop <> wrote:
    >> Alf P. Steinbach wrote:
    >>> * desktop:
    >>>> This page:

    >
    >>>> http://www.eptacom.net/pubblicazioni/pub_eng/mdisp.html

    >
    >>>> start with the line: "Virtual functions allow polymorphism on a single
    >>>> argument". What does that exactly mean?

    >
    >>>> I guess it has nothing to do with making multiple arguments in a
    >>>> declaration like:

    >
    >>>> virtual void setId(int a, int b) {id = a+b;}

    >
    >>> Right. The argument in question is the implicit this-pointer, the
    >>> object you're calling the member function on. And what it means is that
    >>> what member function implementation to call is selected based on the run
    >>> time type of that argument.

    >
    >> Ok so the argument in question is "obj" in this context:

    >
    >> obj.callMe()

    >
    >> where obj is the object that the member function "callMe()" is called upon.

    >
    > Yes. In C++ syntax. Conceptually, this can be mapped to
    > callMe( obj ), with polymorphism always occuring on the first
    > object (in C++).
    >
    > Now consider something like "obj.callMe( arg )". Conceptually,
    > this would be "callMe( obj, arg )". In C++, polymorphism only
    > works on the first argument here. In other languages, the
    > actual function called can depend on the dynamic type of both
    > arguments, e.g. in CLOS: "(callMe obj arg)", the actual function
    > called can depend on the type of obj, the type of arg or both
    > (or neither).
    >
    > As Alf pointed out, this can get a bit hairy: if both arguments
    > can have 10 different types, this means 100 different functions.
    > Which you have to write. And to add a new type, you practically
    > have to know all of the existing types, in order to add all of
    > the additional functions.
    >
    > In practice, in C++, this can be implemented by calling a
    > virtual function on the first object, and having it call a
    > virtual function on the second. But with the constraint that
    > the base class has to know of the existance of all of the
    > derived types.
    >




    Ok, if we have obj.callMe(arg) the "first" makes sure to find the right
    "owner" of obj and uses the virtual functionality to accomplish this.

    The "owner object" has as many callMe(arg) functions (specified as
    virtual in an abstract base-class) as there are different types of "arg"
    objects. In the owner object it could look like this:

    callMe(Ball) {}
    callMe(Plane) {}
    callMe(Bus) {}
    callMe(Toy) {}


    The "second" call as I understand is just a matter of finding the right
    function matching the called argument "arg" through the overloaded
    function callMe.

    You say that multiple-arg polymorphism deals with two virtual calls, but
    are the second not just a regular call to an overloaded function? Or
    does it only hold for this example?
     
    desktop, May 11, 2007
    #7
  8. desktop

    James Kanze Guest

    On May 11, 3:52 pm, desktop <> wrote:
    > James Kanze wrote:
    > > On May 11, 12:07 am, desktop <> wrote:
    > > In practice, in C++, this can be implemented by calling a
    > > virtual function on the first object, and having it call a
    > > virtual function on the second. But with the constraint that
    > > the base class has to know of the existance of all of the
    > > derived types.


    > Ok, if we have obj.callMe(arg) the "first" makes sure to find the right
    > "owner" of obj and uses the virtual functionality to accomplish this.


    > The "owner object" has as many callMe(arg) functions (specified as
    > virtual in an abstract base-class) as there are different types of "arg"
    > objects. In the owner object it could look like this:


    > callMe(Ball) {}
    > callMe(Plane) {}
    > callMe(Bus) {}
    > callMe(Toy) {}


    > The "second" call as I understand is just a matter of finding the right
    > function matching the called argument "arg" through the overloaded
    > function callMe.


    > You say that multiple-arg polymorphism deals with two virtual calls, but
    > are the second not just a regular call to an overloaded function?


    No. The first virtual function call resolves the type of the
    first object. It ends up in a function of this type, with a
    fully typed this. It then calls a virtual function on the
    second object, which takes the resolved type as argument. All
    of the functions must be present and virtual in the base class,
    so we get something like:

    class Vehicule
    {
    public:
    virtual void collideWith( Vehicule& other ) = 0 ;

    virtual void collideWith( Car& other ) = 0 ;
    virtual void collideWith( Bus& other ) = 0 ;
    virtual void collideWith( Truck& other ) = 0 ;
    // ...
    } ;

    class Car : public Vehicule
    {
    public:
    virtual void collideWith( Vehicule& other )
    {
    other.collideWith( *this ) ;
    }

    virtual void collideWith( Car& other )
    {
    // Car vs. Car...
    }
    virtual void collideWith( Bus& other )
    {
    // Bus vs. Car...
    }
    virtual void collideWith( Truck& other )
    {
    // Truck vs. Car...
    }
    } ;

    Vehicule* pCar = new Car ;
    Vehicule* pBus = new Bus ;

    pCar->collideWith( *pBus ) ;

    So what happens here. First, the compiler decides *statically*
    which function is to be called (overload resolution), in
    Vehicule. Since the argument has the static type Vehicule
    (because it is a Vehicule* which is dereferences), the compiler
    chooses Vehicule::collideWith( Vehicule& ). Overload resolution
    is *always* based on the static type. Since this function is
    declared virtual, virtual dispatch occurs, and as a result, we
    end up in Car::collideWith( Vehicule& ). But all this does is
    call collideWith on the other object. So we start over:
    overload resolution in the base class. Except that this time,
    the argument is *this, this is a Car* (and not a Vehicule*), so
    oeverload resolution chooses Vehicule::collideWith( Car& ) (and
    not Vehicule::collideWith( Vehicule& ), as it did the first
    time. Again, this function is declared virtual, so virtual
    dispatch is applied to "other"; since the actual type is a Bus,
    we end up in Bus::collideWith( Car& ) (which I haven't shown).

    Basically, each dynamic dispatch works on a single object.
    Using this pattern, the first dispatch serves to get us into a
    context where the static type is the same as that as the first
    object. We then inverse the call, so that the second static
    dispatch will call a function based on the type of what was
    originally the second argument, with the already resolved type
    being used to choose the correct function, by means of overload
    resolution.

    --
    James Kanze (Gabi Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 11, 2007
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.

Share This Page