Topic: Virtual functions: what is wrong in this code example?


Author: Robert Klemme <bob.news@gmx.net>
Date: Wed, 12 Jun 2002 18:24:27 GMT
Raw View

Eugene schrieb:
> The way I understand 10.3/2 of the standard, only the base class
> lattice of a given class is taken into account when determining
> virtual function overriders.
> It does not matter how many subobjects are there -
> virtual functions belong to a class.
> All instances share a single vtable, after all.

correct.  but the compiler cannot determine, which method to put
there.  is it the one from One or the one from Two?  there is
only one implementation (the one in class Impl) from a code point
of view but there are two effective versions, i.e., the code will
have different effects when executed on sub object One or Two.

btw: i think you would have reached the same effect (or problemc
for that matter) without class Iface by directly declaring value
virtual in class Impl.  the virtual inheritance between Iface and
Impl does not affect the example because the next inheritances
are not virtual.

regards

 robert

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.de (James Kanze)
Date: Wed, 12 Jun 2002 18:26:45 GMT
Raw View
eugenea@bremer-inc.com (Eugene) wrote in message
news:<9d3f4ccd.0206111142.3431118@posting.google.com>...
> "Barry Margolin" <barmar@genuity.net> wrote in message
> news:9GrN8.19$9n1.1646@paloalto-snr2.gtei.net...

> > In article <9d3f4ccd.0206111009.26c49c6@posting.google.com>,
> > Eugene <eugenea@bremer-inc.com> wrote:
> > >This is not the case in my example. There is a final overrider -
> > >Impl::value().

> > But there are two instances of the Impl sub-object, one inherited
> > via One, the other inherited via Two, because you didn't use
> > virtual inheritance.

> I pretty much aware of that, and this is intentional in this
> example.  There are two Impl class subobjects, all right.  I am not
> trying to prove that the code in the example is well formed.  The
> question is whether it is a "non-unique final overrider" issue.

> The way I understand 10.3/2 of the standard, only the base class
> lattice of a given class is taken into account when determining
> virtual function overriders.

Why?  We are talking about a *final* overrider -- the function which
would be called from the most derived class.

> It does not matter how many subobjects are there - virtual functions
> belong to a class.  All instances share a single vtable, after all.

I'm not sure about your point here.  As I pointed out in my response
to your posting in clc++m, the most derived class will typically have
three vtables (in this case), all different.  While it is true that a
call to One::value() and to Two::value() will call the same function,
it will call it with a different this pointer.  The information
necessary to do this is contained (in typically implementations) in
the vtable, and is *different* for the two functions.  So what does
the compiler put in the vtable for Dual?

--
James Kanze                                mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                    Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(0)69 63198627

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: eugenea@bremer-inc.com (Eugene)
Date: Wed, 12 Jun 2002 18:27:50 GMT
Raw View
Hyman Rosen <hyrosen@mail.com> wrote in message news:<3D0673EE.3080603@mail.com>...
> Eugene wrote:
>  > only the base class lattice...
> > It does not matter how many subobjects are there...
>
> I do not see why you believe that the base class lattice
> is in any way a different thing from the set of class
> subobjects that form an object of that class.

Well, it depends on how you prefer to draw it :)

>Without
> virtual inheritance, the base class lattice has multiple
> instances of the Impl class, in exactly the same way that
> the ultimate object has multiple subobjects of Impl type.

I was referring to a plain class inheritance diagram that is not
concerned with class instances and contains a single box per class
regardless of what kind of inheritance is used.

I came to realize now that this is inadequate for analyzing virtual
function overriding because a virtual function needs to carry
information about the subobject. In other words a vtable entry should
provide virtual function implementation address along with the address
of the base class subobject (or the most derived object).

Thank you for your patience :)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: eugenea@bremer-inc.com (Eugene)
Date: Mon, 10 Jun 2002 23:24:13 GMT
Raw View
The return from get_value() is apparently ambigous.
Many compilers (including Comeau C++ 4.3.0) compile it successfully.
gcc says that there is no final overrider, but is it so?
What does the standard says about the code below?

class Iface
{
public:
    virtual int value() const = 0;
};

class Impl: virtual public Iface
{
public:
    Impl(int val): value_(val)
    {}

    int value() const
    {
        return value_;
    }

private:
    int value_;
};

class One: public Impl
{
public:
    One(): Impl(1)
    {}
};

class Two: public Impl
{
public:
    Two(): Impl(2)
    {}
};

class Dual: public One, public Two
{};

int get_value(const Dual& obj)
{
    const Iface& iface = obj;
    return iface.value();
}

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Marek Przeczek <marek.przeczek@st.ms.mff.cuni.cz>
Date: Tue, 11 Jun 2002 15:02:51 GMT
Raw View
suppose you want virtual inheritance like this:
Dual class shares (classes One and Two as subclasses) the same data of Impl
(in One and Two)

  Iface
   |
  Impl
 /    \    <=  virtual inheritance _MUST_ be here !!!
One   Two
 \    /
  Dual

so changes in your example:

class Impl: public Iface {  // NO VIRTUAL INHERITANCE HERE !
}

class One: virtual public Impl { // BUT HERE !
           -------
}

class Two: virtual public Impl { // AND HERE !
           -------
}




that's all :-)

marek





Eugene wrote:

> The return from get_value() is apparently ambigous.
> Many compilers (including Comeau C++ 4.3.0) compile it successfully.
> gcc says that there is no final overrider, but is it so?
> What does the standard says about the code below?
>
> class Iface
> {
> public:
>     virtual int value() const = 0;
> };
>
> class Impl: virtual public Iface
> {
> public:
>     Impl(int val): value_(val)
>     {}
>
>     int value() const
>     {
>         return value_;
>     }
>
> private:
>     int value_;
> };
>
> class One: public Impl
> {
> public:
>     One(): Impl(1)
>     {}
> };
>
> class Two: public Impl
> {
> public:
>     Two(): Impl(2)
>     {}
> };
>
> class Dual: public One, public Two
> {};
>
> int get_value(const Dual& obj)
> {
>     const Iface& iface = obj;
>     return iface.value();
> }
>
> ---
> [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
> [ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
> [              --- Please see the FAQ before posting. ---               ]
> [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Jim Fischer" <jfischer110@attbi.com>
Date: Tue, 11 Jun 2002 16:16:01 GMT
Raw View
"Eugene" <eugenea@bremer-inc.com> wrote in message
news:9d3f4ccd.0206101520.418d22b4@posting.google.com...
> The return from get_value() is apparently ambigous.
> Many compilers (including Comeau C++ 4.3.0) compile it successfully.
> gcc says that there is no final overrider, but is it so?

gcc is correct.


> What does the standard says about the code below?
>
> class Iface
> {
> public:
>     virtual int value() const = 0;
> };
>
> class Impl: virtual public Iface
> {
> public:
>     Impl(int val): value_(val)
>     {}
>
>     int value() const
>     {
>         return value_;
>     }
>
> private:
>     int value_;
> };
>
> class One: public Impl
> {
> public:
>     One(): Impl(1)
>     {}
> };
>
> class Two: public Impl
> {
> public:
>     Two(): Impl(2)
>     {}
> };
>
> class Dual: public One, public Two
> {};
>
> int get_value(const Dual& obj)
> {
>     const Iface& iface = obj;
>     return iface.value();
> }
>

The code sample above is similar to the code sample shown in 10.3p10 of the
C++ standard.

<quote>
10.3  Virtual Functions
....
10  The following example shows a function that does not have a unique final
overrider:

    struct A {
        virtual void f();
    };

    struct VB1 : virtual A {      // note virtual derivation
        void f();
    };

    struct VB2 : virtual A {
        void f();
    };

    struct Error : VB1, VB2 {      // ill-formed
    };

    struct Okay : VB1, VB2 {
        void f();
    };

Both VB1::f and VB2::f override A::f but there is no overrider of both of
them in class Error. This
example is therefore illformed. Class Okay is well formed, however, because
Okay::f is a final overrider.
</quote>


Jim



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: eugenea@bremer-inc.com (Eugene)
Date: Tue, 11 Jun 2002 18:12:54 GMT
Raw View
I would like to mention to the posters pointing to the examples in
10.3 of the standard, that I am aware of them, and that is what
actually has led me to my example. Each of those examples present a
class that does not have a final overrider when the class hierarchy of
its base classes is considered.
This is not the case in my example. There is a final overrider -
Impl::value().

This example has been posted to comp.c++.moderated under the subject
"Is this a valid C++ code".
Below is my response to one of the posts.
I would appreciate very much if I hear a comment on that from a C++
standard expert.



"James Kanze" <kanze@gabi-soft.de> wrote in message news:d6651fb6.0206110154.c15b5f8@posting.google.com...
> I think you should get an error if you try and instantiate Dual, but
> you don't do that here.

It instantiates and executes all right on MSVC.

> If I instantiate Dual, something must go into the vtable as overrider
> for the function IFace::value.  It is ambiguous as to what.

No it is not!
It is Impl::value() which is the only implementation of value() in
this example.
You just brought up the most powerful argument to support my point.

Thank you very much, James! :))

Although vtable implementation of polymorphism is not mandated
by the standard, it is the most common (if not the only)
implementation and provides the perfect illustration of the concept.

Come to think of it.
Virtual function overriding is implemented in terms of vtables.
There is a single vtable for each polymorphyc class.
All instances of a class (most derived objects as well as base class
subobjects) share the same vtable.
That is why only the hierarchy of the base classes of the object class
needs to be considered for the final overrider determination.

There is a final overrider for class Dual, pointer to which is located
in the vtable of class Impl.

When a virtual function is called the following steps have to be
performed:
    1) the final overrider is located;
    2) <this> pointer to the current type is converted to the pointer
       to the final overrider class type;
    3) the resulting <this> pointer is passed to the final overrider
       along with the function arguments;

Now let go back to our example.
There is no problem performing step 1.

A problem arises only when it comes to the step 2 because there are
two base class subobjects of class Impl in an object of class Dual,
and there is no unique conversion from  Iface* to Impl*, namely,
dynamic_cast<const Impl*>(&iface) is ill formed according to 5.2.7/8.

(BTW, sadly enough, the line  above compiles on both MSVC an Comeau
online compiler)
      (http://www.comeaucomputing.com/tryitout/)

This is the root of the problem - not the final overrider.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: eugenea@bremer-inc.com (Eugene)
Date: Tue, 11 Jun 2002 18:54:39 GMT
Raw View
> and there is no unique conversion from  Iface* to Impl*, namely,
> dynamic_cast<const Impl*>(&iface) is ill formed according to 5.2.7/8.
>
> (BTW, sadly enough, the line  above compiles on both MSVC an Comeau
> online compiler)
>       (http://www.comeaucomputing.com/tryitout/)

I was not thinking when I wrote that. Shame on me! :).
Of course it is determined only at run_time!

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Barry Margolin <barmar@genuity.net>
Date: Tue, 11 Jun 2002 19:04:24 GMT
Raw View
In article <9d3f4ccd.0206111009.26c49c6@posting.google.com>,
Eugene <eugenea@bremer-inc.com> wrote:
>I would like to mention to the posters pointing to the examples in
>10.3 of the standard, that I am aware of them, and that is what
>actually has led me to my example. Each of those examples present a
>class that does not have a final overrider when the class hierarchy of
>its base classes is considered.
>This is not the case in my example. There is a final overrider -
>Impl::value().

But there are two instances of the Impl sub-object, one inherited via One,
the other inherited via Two, because you didn't use virtual inheritance.

--
Barry Margolin, barmar@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: eugenea@bremer-inc.com (Eugene)
Date: Tue, 11 Jun 2002 21:32:22 GMT
Raw View
"Barry Margolin" <barmar@genuity.net> wrote in message news:9GrN8.19$9n1.1646@paloalto-snr2.gtei.net...
> In article <9d3f4ccd.0206111009.26c49c6@posting.google.com>,
> Eugene <eugenea@bremer-inc.com> wrote:
> >This is not the case in my example. There is a final overrider -
> >Impl::value().
>
> But there are two instances of the Impl sub-object, one inherited via One,
> the other inherited via Two, because you didn't use virtual inheritance.

Barry,
I pretty much aware of that, and this is intentional in this example.
There are two Impl class subobjects, all right.
I am not trying to prove that the code in the example is well formed.
The question is whether it is a "non-unique final overrider" issue.

The way I understand 10.3/2 of the standard, only the base class
lattice of a given class is taken into account when determining
virtual function overriders.
It does not matter how many subobjects are there -
virtual functions belong to a class.
All instances share a single vtable, after all.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Hyman Rosen <hyrosen@mail.com>
Date: Tue, 11 Jun 2002 22:06:43 GMT
Raw View
Eugene wrote:
 > only the base class lattice...
> It does not matter how many subobjects are there...

I do not see why you believe that the base class lattice
is in any way a different thing from the set of class
subobjects that form an object of that class. Without
virtual inheritance, the base class lattice has multiple
instances of the Impl class, in exactly the same way that
the ultimate object has multiple subobjects of Impl type.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]