Topic: Why isn't `&Derived::virt' a `void(Base::*)()'?
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Wed, 9 Jun 1993 07:15:16 GMT Raw View
In article <solomon.739535433@cs.wisc.edu> solomon@gjetost.cs.wisc.edu (Marvin Solomon) writes:
>
>While we're on the subject, he're my favorite "so you think you knew C++"
>question.
> struct Base1 { virtual void foo(); }
> struct Base2 { void foo(); }
> struct Derived : Base1, Base2 { void foo(); }
>
>Is Derived::foo virtual? Please cite chapter and verse from the ARM (or
>the ANSI standard :-) to support your answer.
Yes. That is an enigma. Many similar ones exist also.
struct Base1 { virtual void foo(); }
struct Base2 { void foo(); }
struct Derived : Base1, Base2 { static void foo(); }
Is this valid code, or must a compiler issue a diagnostic for it?
struct Base1 { virtual void foo() = 0; }
struct Base2 { virtual void foo(); }
struct Derived : Base1, Base2 { }
This this valid code? Is `foo' abstract with respect to `Derived'?
I only hope that someone decides these things someday... preferably this
decade.
--
// Ron ("Loose Cannon") Guilmette uucp: ...uunet!lupine!segfault!rfg
//
// "On the one hand I knew that programs could have a compelling
// and deep logical beauty, on the other hand I was forced to
// admit that most programs are presented in a way fit for
// mechanical execution, but even if of any beauty at all,
// totally unfit for human appreciation."
// -- Edsger W. Dijkstra
Author: jamshid@emx.cc.utexas.edu (Jamshid Afshar)
Date: 4 Jun 1993 18:34:06 -0500 Raw View
We all know that given:
struct B { virtual void f(); };
struct D : B {};
`&D::f' is of type `void(Base::*)()' and *not* `void(Derived::*)()'
(see ARM 5.3). The class type of a member pointer is not necessarily
the type of the class-name-qualifier used when taking the address.
I'm wondering why the type of `&D::f' changes when `D' overrides `f()':
struct B { virtual void f(); };
struct D : B { virtual void f(); };
void (B::*p) = &D::f; // error, why?
After all, the assignment is type-safe since member function pointers
always do virtual calls, and any `B' will have an `f()'. It seems to
me that `D's function named "f" is really a function "belonging" to
`B', so `&D::f' should at least convert to a `void(B::*)()'.
Comments? I haven't come across any real-world problems with the
current rules; I just curious if there's some flaws in my reasoning.
Jamshid Afshar
jamshid@emx.utexas.edu
Author: pete@borland.com (Pete Becker)
Date: Sat, 5 Jun 1993 00:31:38 GMT Raw View
In article <1uom5eINNtrl@emx.cc.utexas.edu> jamshid@emx.cc.utexas.edu (Jamshid Afshar) writes:
>We all know that given:
> struct B { virtual void f(); };
> struct D : B {};
>`&D::f' is of type `void(Base::*)()' and *not* `void(Derived::*)()'
>(see ARM 5.3). The class type of a member pointer is not necessarily
>the type of the class-name-qualifier used when taking the address.
>
>I'm wondering why the type of `&D::f' changes when `D' overrides `f()':
>
> struct B { virtual void f(); };
> struct D : B { virtual void f(); };
> void (B::*p) = &D::f; // error, why?
>
>After all, the assignment is type-safe since member function pointers
>always do virtual calls, and any `B' will have an `f()'. It seems to
>me that `D's function named "f" is really a function "belonging" to
>`B', so `&D::f' should at least convert to a `void(B::*)()'.
>
The problem is that this doesn't work for non-virtual functions.
The rule may be broader than necessary, but it's much simpler this way.
Imagine the maintenance headaches if changing the f() in base from virtual to
non-virtual made the assignment illegal...
-- Pete
Author: marc@offline.be (Marc Duponcheel)
Date: 5 Jun 93 22:36:43 PST Raw View
> struct B { virtual void f(); };
> struct D : B { virtual void f(); };
> void (B::*p) = &D::f; // error, why?
But what if :
struct B { virtual void f(); };
struct D : B { virtual void f(); virtual void g(); };
void (B::*p) = &D::g; // oops ! B has no g ...
-- marc.
Author: jamshid@emx.cc.utexas.edu (Jamshid Afshar)
Date: 6 Jun 1993 03:02:04 -0500 Raw View
[I reply to both Pete's and Marc's articles here]
In article <marc.04ec@offline.be> marc@offline.be (Marc Duponcheel) writes:
In article <1uom5eINNtrl@emx.cc.utexas.edu> jamshid@emx.cc.utexas.edu (Jamshid Afshar) writes:
|> struct B { virtual void f(); };
|> struct D : B { virtual void f(); };
|> void (B::*p) = &D::f; // error, why?
|But what if :
|struct B { virtual void f(); };
|struct D : B { virtual void f(); virtual void g(); };
|void (B::*p) = &D::g; // oops ! B has no g ...
I guess I should have made it more clear that I was only discussing
virtual functions that are defined in the base class (not those
introduced in the derived class). It just seems more logical to me
that &Foo::virt should be of type "pointer to member of T" where T is
the class that "first" declares the virtual function (instead of the
"last" one to override it).
In article <1993Jun5.003138.20163@borland.com> pete@borland.com (Pete Becker):
>The rule may be broader than necessary, but it's much simpler this way.
>Imagine the maintenance headaches if changing the f() in base from virtual to
>non-virtual made the assignment illegal...
The scenario you describe doesn't seem like a very convincing reason.
The compiler error about the then illegal conversion isn't anywhere
near as serious a problem as the run-time errors caused by code
expecting the function to be polymorphic.
Although &Base::virt and &Derived::virt "mean" the exact same thing,
C++ makes them different types when (and only when) Derived overrides
'virt'. Don't get me wrong -- I don't consider this topic clear-cut
in my favor. I was just hoping there were more solid reasons for the
current state of affairs.
Jamshid Afshar
jamshid@emx.utexas.edu
Author: jamshid@emx.cc.utexas.edu (Jamshid Afshar)
Date: 7 Jun 1993 21:44:34 -0500 Raw View
In article <9315721.12426@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>jamshid@emx.cc.utexas.edu (Jamshid Afshar) writes:
>>It just seems more logical to me
>>that &Foo::virt should be of type "pointer to member of T" where T is
>>the class that "first" declares the virtual function (instead of the
>>"last" one to override it).
>
> struct Base1 { virtual void foo(); }
> struct Base2 { virtual void foo(); }
> struct Derived : Base1, Base2 { virtual void foo(); }
>Which is the "first" class to declare foo()?
Ahh, good reason. I've always thought the above code is an error. I
thought the technique used to "rename" virtual functions that have the
same name when using MI (eg, GunSlinger::draw() and Shape::draw()) was
developed because the above is an error. Actually, the "renaming"
technique was developed to allow a programmer to individually override
the functions.
Thanks, Jamshid Afshar
jamshid@emx.utexas.edu
Author: solomon@gjetost.cs.wisc.edu (Marvin Solomon)
Date: Tue, 8 Jun 1993 10:30:33 GMT Raw View
jamshid@emx.cc.utexas.edu (Jamshid Afshar) writes:
>In article <9315721.12426@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>>jamshid@emx.cc.utexas.edu (Jamshid Afshar) writes:
>>>It just seems more logical to me
>>>that &Foo::virt should be of type "pointer to member of T" where T is
>>>the class that "first" declares the virtual function (instead of the
>>>"last" one to override it).
>>
>> struct Base1 { virtual void foo(); }
>> struct Base2 { virtual void foo(); }
>> struct Derived : Base1, Base2 { virtual void foo(); }
>>Which is the "first" class to declare foo()?
While we're on the subject, he're my favorite "so you think you knew C++"
question.
struct Base1 { virtual void foo(); }
struct Base2 { void foo(); }
struct Derived : Base1, Base2 { void foo(); }
Is Derived::foo virtual? Please cite chapter and verse from the ARM (or
the ANSI standard :-) to support your answer.
--
Marvin Solomon, Professor
Computer Sciences Department
University of Wisconsin
1210 W. Dayton St., Madison WI, USA
(608) 263-2844
solomon@cs.wisc.edu