Topic: Is this code legal C++?
Author: bgibbons@taligent.com (Bill Gibbons)
Date: Tue, 9 Aug 1994 23:48:32 GMT Raw View
In article <31u6cs$do0@ritz.cec.wustl.edu>, jaf3@ritz.cec.wustl.edu
(John Andrew Fingerhut) wrote (more or less):
> struct B1 {
> int b1;
> int f1();
> };
>
> struct B2 {
> int b2;
> int f2();
> };
>
> struct D : B1, B2 {
> int d;:
> int f3();
> };
>
> D d;
> B2 *b2 = &d;
>
> typedef int (B2::*mfp)();
>
> /*line 1*/ mfp mfp1 = (mfp) &D::f3;
> /*line 2*/ mfp mfp2 = &B2::f2;
>
> /*line 3*/ (b2->*mfp1)();
> /*line 4*/ (b2->*mfp2)();
>
> In order for "this" to be set correctly in f3, b2 must be converted to a "D"
> when called through the mfp1. Because of the assumed memory layout, that
> implies a change in the address. The only way that I can see this working is
> if the assignment in line 1 stores the offset difference between the
> address of a "D" and the address of a "B2" and uses that stored offset in
> the call on line 3.
> This offset would be 0 for lines 2 and 4. Is the result of this code
> defined by the current working paper? Note that none of the functions in my
> example are virtual so there should be no vtable.
This example definitely has undefined behavior under the currently adopted
rules in the working paper.
But some implementations do allow this kind of cast and dereference,
even with virtual functions. It's relatively easy to implement; as John
points out, casting a pointer to member may involve recording offset
information for use at the point of dereference.
I've been pushing for a change to the working paper to make this kind
of code valid; the gist of the change is:
A pointer to member may be cast to a class containing or inheriting
the original member, or to a nonvirtual base class of
the class containing the original member. When a pointer to
member is dereferenced, the actual object must contain the member
to which the pointer to member refers.
This subject did not get wide enough discussion at the last meeting to
form a consensus. Part of the problem was that there were other
pointer to member issues which needed to be dealt with first.
Part of the problem was that many people just aren't interested in
pointers to members.
I'll try again in November.
Bill Gibbons
bgibbons@taligent.com
Author: pstemari@Dayton.fsp.com (Paul Ste. Marie)
Date: Sun, 31 Jul 1994 18:45:38 GMT Raw View
In article <JASON.94Jul27182042@deneb.cygnus.com>,
Jason Merrill <jason@cygnus.com> wrote:
>>>>>> Pete Becker <pete@genghis.interbase.borland.com> writes:
>
>> In article <CtLuIz.FIn@ucc.su.oz.au>,
>> John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
>>> In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>>>>> memfnp m = (memfnp) &D::fn; // Line 6
[snip--and the attributions seem to be hopelessly mangled]
>I think John's point was that 'm' cannot be subsequently applied to ANY
>expression of type 'B' (even one which is part of an object of type 'D')
>without causing undefined behavior. Thus, the cast is not useful.
Why not? If you apply the offset manipulations in the same fashion
for explicit upcasts that you do for explicit downcasts (leaving the
issue of char* vs void* aside), there's no reason why code such as:
class Base {
public:
virtual int foo();
virtual int spam();
};
class Derived {
public:
virtual int foo();
virtual int bar();
virtual int bletch();
};
Derived D;
Base *B = &D;
typedef int (Base::*mfp)();
mfp mfp1 = (mfp) &Derived::foo;
mfp mfp2 = (mfp) &Derived::bar;
mfp mfp3 = (mfp) &Derived::bletch;
(B->*mfp1)();
(B->*mfp2)();
(B->*mfp3)();
The only real requirement is that the vtable for D contain entries for
all virtual member functions of B, not just those that have been
overridden.
--Paul
Author: imp@boulder.parcplace.com (Warner Losh)
Date: Mon, 1 Aug 1994 19:29:38 GMT Raw View
In article <JASON.94Jul27182042@deneb.cygnus.com> jason@cygnus.com
(Jason Merrill) writes:
>>>>>> memfnp m = (memfnp) &D::fn; // Line 6
>I think John's point was that 'm' cannot be subsequently applied to ANY
>expression of type 'B' (even one which is part of an object of type 'D')
>without causing undefined behavior. Thus, the cast is not useful.
This restriction seems overly burdonsome.
Are you telling me that if I cast to a base class, I can't make calls
through that pointer even when I know I'm making them on behalf of a D
object? Eg, it must be cast back to a void *(D::*)() before I can
call it? How on earth can I do this in the context of a generic
callback mechanism when I have no way of knowing all the possible
classes for which this will be used?
I can't say, later in my code:
D *d = new D;
B *b = d;
(b->*m)();
Warner
--
Warner Losh imp@boulder.parcplace.COM ParcPlace Boulder
"... but I can't promote you to "Prima Donna" unless you demonstrate a few
more serious personality disorders"
Author: pete@genghis.interbase.borland.com (Pete Becker)
Date: Tue, 2 Aug 1994 01:02:33 GMT Raw View
In article <CtvFHF.6s0@boulder.parcplace.com>,
Warner Losh <imp@boulder.parcplace.com> wrote:
>In article <JASON.94Jul27182042@deneb.cygnus.com> jason@cygnus.com
>(Jason Merrill) writes:
>>>>>>> memfnp m = (memfnp) &D::fn; // Line 6
>>I think John's point was that 'm' cannot be subsequently applied to ANY
>>expression of type 'B' (even one which is part of an object of type 'D')
>>without causing undefined behavior. Thus, the cast is not useful.
>
>This restriction seems overly burdonsome.
>
>Are you telling me that if I cast to a base class, I can't make calls
>through that pointer even when I know I'm making them on behalf of a D
>object? Eg, it must be cast back to a void *(D::*)() before I can
>call it? How on earth can I do this in the context of a generic
>callback mechanism when I have no way of knowing all the possible
>classes for which this will be used?
>
>I can't say, later in my code:
> D *d = new D;
> B *b = d;
> (b->*m)();
>
Suppose, that I cast a pointer-to-function to a void *. Would you expect to be able to use it as a pointer to function without telling the compiler
what it really is?
-- Pete
Author: kanze@us-es.sel.de (James Kanze US/ESC 60/3/164 #71425)
Date: 02 Aug 1994 16:54:15 GMT Raw View
In article <CtoynA.71o@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max
Skaller) writes:
|> An old style cast had better be functionally equivalent to either
|> a static_cast or a reinterpret_cast.
Or a const_cast?
--
James Kanze email: kanze@lts.sel.alcatel.de
GABI Software, Sarl., 8 rue des Francs Bourgeois, F-67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung
Author: jaf3@ritz.cec.wustl.edu (John Andrew Fingerhut)
Date: 5 Aug 1994 15:11:08 -0500 Raw View
In article <CttIs3.A03@dayton.fsp.com>,
Paul Ste. Marie <pstemari@Dayton.fsp.com> wrote:
:In article <JASON.94Jul27182042@deneb.cygnus.com>,
:Jason Merrill <jason@cygnus.com> wrote:
:>>>>>> Pete Becker <pete@genghis.interbase.borland.com> writes:
:>
:>> In article <CtLuIz.FIn@ucc.su.oz.au>,
:>> John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
:>>> In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
:>>>>>> memfnp m = (memfnp) &D::fn; // Line 6
:
: [snip--and the attributions seem to be hopelessly mangled]
:
:>I think John's point was that 'm' cannot be subsequently applied to ANY
:>expression of type 'B' (even one which is part of an object of type 'D')
:>without causing undefined behavior. Thus, the cast is not useful.
:
:Why not? If you apply the offset manipulations in the same fashion
:for explicit upcasts that you do for explicit downcasts (leaving the
:issue of char* vs void* aside), there's no reason why code such as:
:
:class Base {
: public:
: virtual int foo();
: virtual int spam();
: };
:
:class Derived {
: public:
: virtual int foo();
: virtual int bar();
: virtual int bletch();
: };
:
:Derived D;
:Base *B = &D;
:
:typedef int (Base::*mfp)();
:
:mfp mfp1 = (mfp) &Derived::foo;
:mfp mfp2 = (mfp) &Derived::bar;
:mfp mfp3 = (mfp) &Derived::bletch;
:
:(B->*mfp1)();
:(B->*mfp2)();
:(B->*mfp3)();
:
:The only real requirement is that the vtable for D contain entries for
:all virtual member functions of B, not just those that have been
:overridden.
:
: --Paul
In the original code sample, the function was not virtual.
Try this one:
class B1 {
int b1;
public:
int f1();
};
class B2 {
int b2;
public:
int f2();
};
class D : public B1, public B2 {
int d;
public:
int f3();
};
// Assume memory layout:
// +------+
// | B1 |
// +------+
// | B2 |
// +------+
// | D |
// +------+
D d;
B2 *b2 = &d;
typedef int (B2::*mfp)();
/*line 1*/ mfp mfp1 = (mfp) &D::f3;
/*line 2*/ mfp mfp2 = &B2::f2;
/*line 3*/ (b2->*mfp1)();
/*line 4*/ (b2->*mfp2)();
In order for "this" to be set correctly in f3, b2 must be converted to a "D"
when called through the mfp1. Because of the assumed memory layout, that
implies a change in the address. The only way that I can see this working is
if the assignment in line 1 stores the offset difference between the address of
a "D" and the address of a "B2" and uses that stored offset in the call on line
3. This offset would be 0 for lines 2 and 4. Is the result of this code
defined by the current working paper? Note that none of the functions in my
example are virtual so there should be no vtable.
Stephen Gevers
sg3235@shelob.sbc.com
Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 29 Jul 1994 07:40:21 GMT Raw View
In article <CtM20s.IJC@borland.com> pete@genghis.interbase.borland.com (Pete Becker) writes:
>In article <CtLuIz.FIn@ucc.su.oz.au>,
>John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
>>In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>>>> memfnp m = (memfnp) &D::fn; // Line 6
>>
>> Nope. It is no longer permitted. Resolved at Waterloo.
>>Specifically, a static_cast to a base not containing or inheriting
>>the original member is undefined.
>>
>> A reinterpret_cast is permitted but cant be used without
>>casting back.
>
> Looks to me like this code uses an old-style cast, not a static cast.
> -- Pete
An old style cast had better be functionally equivalent to either
a static_cast or a reinterpret_cast.
--
JOHN (MAX) SKALLER, INTERNET:maxtal@suphys.physics.su.oz.au
Maxtal Pty Ltd,
81A Glebe Point Rd, GLEBE Mem: SA IT/9/22,SC22/WG21
NSW 2037, AUSTRALIA Phone: 61-2-566-2189
Author: pete@genghis.interbase.borland.com (Pete Becker)
Date: Wed, 27 Jul 1994 15:45:03 GMT Raw View
In article <rfgCtLDAn.EKx@netcom.com>,
Ronald F. Guilmette <rfg@netcom.com> wrote:
>In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>>> memfnp m = (memfnp) &D::fn; // Line 6
>>>
>
>First, as has already been noted, a member of D is *not* necessarily also
>a member of B, and so it is not valid to assign a pointer to a member of
>D to a variable having type `pointer-to-member-of-B'... at least not without
>some explicit cast in there.
>
Sure looks to me like there's an explicit cast...
-- Pete
Author: pete@genghis.interbase.borland.com (Pete Becker)
Date: Wed, 27 Jul 1994 18:00:27 GMT Raw View
In article <CtLuIz.FIn@ucc.su.oz.au>,
John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
>In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>>> memfnp m = (memfnp) &D::fn; // Line 6
>
> Nope. It is no longer permitted. Resolved at Waterloo.
>Specifically, a static_cast to a base not containing or inheriting
>the original member is undefined.
>
> A reinterpret_cast is permitted but cant be used without
>casting back.
Looks to me like this code uses an old-style cast, not a static cast.
-- Pete
Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 27 Jul 1994 15:18:35 GMT Raw View
In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>In article <COLIN.94Jul24222456@rafferty.rafferty.com> colin@rafferty.com (Colin Owen Rafferty) writes:
>>In article <CtCtuy.99r@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>> class B { public: B(); virtual ~B(); };
>>> typedef void *(B::*memfnp)();
>>> class D : public B { public: D() ; virtual ~D();
>>> char *fn();
>>> };
>>> memfnp m = (memfnp) &D::fn; // Line 6
>>
>>The problem with this (as I see it) is that you are implicitly trying to
>>allow a function of a derived class to manipulate an object of a base
>>class.
>
>That isn't what I'm complaining about. That is already 100% legal and
>works in all C++ compilers that I've used.
Nope. It is no longer permitted. Resolved at Waterloo.
Specifically, a static_cast to a base not containing or inheriting
the original member is undefined.
A reinterpret_cast is permitted but cant be used without
casting back.
--
JOHN (MAX) SKALLER, INTERNET:maxtal@suphys.physics.su.oz.au
Maxtal Pty Ltd,
81A Glebe Point Rd, GLEBE Mem: SA IT/9/22,SC22/WG21
NSW 2037, AUSTRALIA Phone: 61-2-566-2189
Author: jason@cygnus.com (Jason Merrill)
Date: Thu, 28 Jul 1994 01:20:42 GMT Raw View
>>>>> Pete Becker <pete@genghis.interbase.borland.com> writes:
> In article <CtLuIz.FIn@ucc.su.oz.au>,
> John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
>> In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>>>> memfnp m = (memfnp) &D::fn; // Line 6
>>
>> Nope. It is no longer permitted. Resolved at Waterloo.
>> Specifically, a static_cast to a base not containing or inheriting
>> the original member is undefined.
>>
>> A reinterpret_cast is permitted but cant be used without
>> casting back.
> Looks to me like this code uses an old-style cast, not a static cast.
I think John's point was that 'm' cannot be subsequently applied to ANY
expression of type 'B' (even one which is part of an object of type 'D')
without causing undefined behavior. Thus, the cast is not useful.
Jason
Author: bgibbons@taligent.com (Bill Gibbons)
Date: Wed, 27 Jul 1994 23:25:41 GMT Raw View
In article <CtLuIz.FIn@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max
Skaller) wrote:
> In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
> >In article <COLIN.94Jul24222456@rafferty.rafferty.com> colin@rafferty.com (Colin Owen Rafferty) writes:
> >>In article <CtCtuy.99r@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
> >>> class B { public: B(); virtual ~B(); };
> >>> typedef void *(B::*memfnp)();
> >>> class D : public B { public: D() ; virtual ~D();
> >>> char *fn();
> >>> };
> >>> memfnp m = (memfnp) &D::fn; // Line 6
> >>
> >>The problem with this (as I see it) is that you are implicitly trying to
> >>allow a function of a derived class to manipulate an object of a base
> >>class.
> >
> >That isn't what I'm complaining about. That is already 100% legal and
> >works in all C++ compilers that I've used.
>
> Nope. It is no longer permitted. Resolved at Waterloo.
> Specifically, a static_cast to a base not containing or inheriting
> the original member is undefined.
>
> A reinterpret_cast is permitted but cant be used without
> casting back.
Yes, but that isn't necessarily the last word.
Before Waterloo, the working paper said nothing about which combinations
of pointer to member casts and dereferences worked. Some people assumed
that a pointer to member had to be cast back to the original type before
the dereference.
The first pointer to member cast proposal at Waterloo specified a
minimal set of conditions for making pointer to member casts work: the
cast must never to go a class which does not contain or inherit the
original
member. There was widespread agreememt that this much should work.
There was a second proposal which specified some additional conditions
under which a cast and subsequent dereference would work. This proposal
would have made the above code valid.
There was no consensus on the second proposal. So the above case
remains undefined, and in effect it is a quality of implementation issue.
(Undefined does not mean that it must not work; rather, that it need not.)
The issue may be reopened at a future meeting, but the working paper is
starting to gel and the chances of getting the second proposal through
are getting slim.
Bill Gibbons
bgibbons@taligent.com
Author: pete@genghis.interbase.borland.com (Pete Becker)
Date: Thu, 28 Jul 1994 16:42:14 GMT Raw View
In article <JASON.94Jul27182042@deneb.cygnus.com>,
Jason Merrill <jason@cygnus.com> wrote:
>>>>>> Pete Becker <pete@genghis.interbase.borland.com> writes:
>
>> In article <CtLuIz.FIn@ucc.su.oz.au>,
>> John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
>>> In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>>>>> memfnp m = (memfnp) &D::fn; // Line 6
>>>
>>> Nope. It is no longer permitted. Resolved at Waterloo.
>>> Specifically, a static_cast to a base not containing or inheriting
>>> the original member is undefined.
>>>
>>> A reinterpret_cast is permitted but cant be used without
>>> casting back.
>
>> Looks to me like this code uses an old-style cast, not a static cast.
>
>I think John's point was that 'm' cannot be subsequently applied to ANY
>expression of type 'B' (even one which is part of an object of type 'D')
>without causing undefined behavior. Thus, the cast is not useful.
>
'm' cannot be used directly. That does not mean that the cast is not
useful. Just cast 'm' back to its original type.
-- Pete
Author: pete@genghis.interbase.borland.com (Pete Becker)
Date: Tue, 26 Jul 1994 00:19:13 GMT Raw View
In article <COLIN.94Jul24222456@rafferty.rafferty.com>,
Colin Owen Rafferty <colin@rafferty.com> wrote:
>In article <CtCtuy.99r@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>
>> I maintain that the code is legal, but I
>> can't seem to find in the ARM or other docs where this is stated.
>
>> class B { public: B(); virtual ~B(); };
>> typedef void *(B::*memfnp)();
>> class D : public B { public: D() ; virtual ~D();
>> char *fn();
>> };
>> memfnp m = (memfnp) &D::fn; // Line 6
>
>> I feel that Line 6 is correct, because the return values are
>> compatible and the rest fo fn is cool. Microsoft's compiler, however,
>> complains:
>> "cast to pointer to member myst be from related pointer to member"
>
>The problem with this (as I see it) is that you are implicitly trying to
>allow a function of a derived class to manipulate an object of a base
>class.
>
>Say you had the following code afterwards:
>
> B b;
> b.*m();
>
>What if D::fn() tried to access member variables that are only part of a D?
>This would cause a real headache at runtime.
>
>Although I don't have my ARM with me, I would be pretty surprised if it
>said that you could do this.
Then prepare to be surprised. The cast is legal.
>
>On the other hand, it would be perfectly valid to cast some B::fn() into
>a member function pointer variable for class D.
In fact, the ARM makes a much stronger statement: the compiler can
do this conversion without a cast.
>
>I guess it can be summed up by saying that since objects of class D can
>be treated as objects of class B (but not vice versa), the functions
>acting on class B can act on class D (and not vice versa).
Which is exactly why the rules are the way they are. The compiler can
implicitly convert to a pointer-member-of-derived, but it cannot implicitly
convert to a pointer-to-member-of-base. With a cast, the conversion to
pointer-to-member-to-base is legal.
-- Pete
Author: colin@rafferty.com (Colin Owen Rafferty)
Date: Tue, 26 Jul 1994 03:44:22 GMT Raw View
In article <CtIu82.Lnn@borland.com> pete@genghis.interbase.borland.com (Pete Becker) writes:
> In article <COLIN.94Jul24222456@rafferty.rafferty.com>, Colin Owen Rafferty <colin@rafferty.com> wrote:
>> In article <CtCtuy.99r@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>> class B { public: B(); virtual ~B(); };
>>> typedef void *(B::*memfnp)();
>>> class D : public B { public: D() ; virtual ~D();
>>> char *fn();
>>> };
>>> memfnp m = (memfnp) &D::fn; // Line 6
>>> I feel that Line 6 is correct, because the return values are
>>> compatible and the rest fo fn is cool. Microsoft's compiler, however,
>>> complains:
>>> "cast to pointer to member myst be from related pointer to member"
>> The problem with this (as I see it) is that you are implicitly trying to
>> allow a function of a derived class to manipulate an object of a base
>> class.
>> Although I don't have my ARM with me, I would be pretty surprised if it
>> said that you could do this.
> Then prepare to be surprised. The cast is legal.
No, it isn't. See your comment four paragraphs below.
>> On the other hand, it would be perfectly valid to cast some B::fn() into
>> a member function pointer variable for class D.
> In fact, the ARM makes a much stronger statement: the compiler can
> do this conversion without a cast.
>> I guess it can be summed up by saying that since objects of class D can
>> be treated as objects of class B (but not vice versa), the functions
>> acting on class B can act on class D (and not vice versa).
> Which is exactly why the rules are the way they are. The compiler can
> implicitly convert to a pointer-member-of-derived, but it cannot implicitly
> convert to a pointer-to-member-of-base. With a cast, the conversion to
> pointer-to-member-to-base is legal.
And this is why I say that the pointer to member of derived cannot be
converted to pointer to member of base.
--
<a href="http://rafferty.com/~colin/">Colin Rafferty</A>
Author: imp@boulder.parcplace.com (Warner Losh)
Date: Mon, 25 Jul 1994 17:11:13 GMT Raw View
In article <COLIN.94Jul24222456@rafferty.rafferty.com> colin@rafferty.com (Colin Owen Rafferty) writes:
>In article <CtCtuy.99r@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>> class B { public: B(); virtual ~B(); };
>> typedef void *(B::*memfnp)();
>> class D : public B { public: D() ; virtual ~D();
>> char *fn();
>> };
>> memfnp m = (memfnp) &D::fn; // Line 6
>
>The problem with this (as I see it) is that you are implicitly trying to
>allow a function of a derived class to manipulate an object of a base
>class.
That isn't what I'm complaining about. That is already 100% legal and
works in all C++ compilers that I've used. The code that I posted was
an abstraction of code that keeps memfnp and objp together so that
you'll really have a D when calling through the member function m.
The salient point is that the return types differ, but are compatible.
The standard, as I've seen it, is a little vauge on what needs to
happen here. I think that the code in question is OK, but the ARM and
Jan working paper both seem to be too vauge on what is and isn't an
acceptible cast. Common implementations accept this case, with the
exception of MSC and Turbo (not Borland) C++ 1.0.
I had been hoping for someone to point me at language that I've
overlooked that requires the return types to be exactly the same. For
ordinary functions, this sort of casting is perfectly legal, but that
language doesn't seem to extend to the code I posted above.
Warner
--
Warner Losh imp@boulder.parcplace.COM ParcPlace Boulder
"... but I can't promote you to "Prima Donna" unless you demonstrate a few
more serious personality disorders"
Author: pete@genghis.interbase.borland.com (Pete Becker)
Date: Tue, 26 Jul 1994 15:40:59 GMT Raw View
In article <COLIN.94Jul25234422@rafferty.rafferty.com>,
Colin Owen Rafferty <colin@rafferty.com> wrote:
>In article <CtIu82.Lnn@borland.com> pete@genghis.interbase.borland.com (Pete Becker) writes:
>> In article <COLIN.94Jul24222456@rafferty.rafferty.com>, Colin Owen Rafferty <colin@rafferty.com> wrote:
>>> In article <CtCtuy.99r@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>
>>>> class B { public: B(); virtual ~B(); };
>>>> typedef void *(B::*memfnp)();
>>>> class D : public B { public: D() ; virtual ~D();
>>>> char *fn();
>>>> };
>>>> memfnp m = (memfnp) &D::fn; // Line 6
>
>>>> I feel that Line 6 is correct, because the return values are
>>>> compatible and the rest fo fn is cool. Microsoft's compiler, however,
>>>> complains:
>>>> "cast to pointer to member myst be from related pointer to member"
>
>>> The problem with this (as I see it) is that you are implicitly trying to
>>> allow a function of a derived class to manipulate an object of a base
>>> class.
>
>>> Although I don't have my ARM with me, I would be pretty surprised if it
>>> said that you could do this.
>
>> Then prepare to be surprised. The cast is legal.
>
>No, it isn't. See your comment four paragraphs below.
>
I saw my comment. In fact, I wrote it. The cast is legal.
>>> On the other hand, it would be perfectly valid to cast some B::fn() into
>>> a member function pointer variable for class D.
>
>> In fact, the ARM makes a much stronger statement: the compiler can
>> do this conversion without a cast.
>
>>> I guess it can be summed up by saying that since objects of class D can
>>> be treated as objects of class B (but not vice versa), the functions
>>> acting on class B can act on class D (and not vice versa).
>
>> Which is exactly why the rules are the way they are. The compiler can
>> implicitly convert to a pointer-member-of-derived, but it cannot implicitly
>> convert to a pointer-to-member-of-base. With a cast, the conversion to
>> pointer-to-member-to-base is legal.
>
>And this is why I say that the pointer to member of derived cannot be
>converted to pointer to member of base.
I don't understand. "With a cast, the conversion to pointer-to-
member-of-base is legal". In what way does that support the claim that the
conversion cannot be done?
It cannot be done implicitly, that is, without an explicit cast. With
an explicit cast, as in the original code example, it is legal and useful.
-- Pete
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Wed, 27 Jul 1994 09:06:23 GMT Raw View
In article <CtIAEp.9tL@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>In article <COLIN.94Jul24222456@rafferty.rafferty.com> colin@rafferty.com (Colin Owen Rafferty) writes:
>>In article <CtCtuy.99r@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
>>> class B { public: B(); virtual ~B(); };
>>> typedef void *(B::*memfnp)();
>>> class D : public B { public: D() ; virtual ~D();
>>> char *fn();
>>> };
>>> memfnp m = (memfnp) &D::fn; // Line 6
>>
>>The problem with this (as I see it) is that you are implicitly trying to
>>allow a function of a derived class to manipulate an object of a base
>>class.
>
>That isn't what I'm complaining about. That is already 100% legal and
>works in all C++ compilers that I've used. The code that I posted was
>an abstraction of code that keeps memfnp and objp together so that
>you'll really have a D when calling through the member function m.
>
>The salient point is that the return types differ, but are compatible.
There is no concept of ``compatible types'' in C++, so your point here
(whatever it was) is not relevant to C++.
>The standard, as I've seen it, is a little vauge on what needs to
>happen here. I think that the code in question is OK, but the ARM and
>Jan working paper both seem to be too vauge on what is and isn't an
>acceptible cast. Common implementations accept this case, with the
>exception of MSC and Turbo (not Borland) C++ 1.0.
The ``common implementations'' that you speak off obviously have at least
two bugs.
First, as has already been noted, a member of D is *not* necessarily also
a member of B, and so it is not valid to assign a pointer to a member of
D to a variable having type `pointer-to-member-of-B'... at least not without
some explicit cast in there.
Also, as you have noted, the return type for `D::fn' is (char*) but an
attempt is being made to assign the address of that to something whose
type is a pointer-to-member-function type where the pointed-at member
function returns a (void*). This incompatability alone (like the other
one I have just mentioned above) is, by itself, enough to make the
assignment/initialization invalid.
So any implementation that accepts the code above has failed to diagnose
at least TWO separate errors that it should have diagnosed.
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -
Author: imp@boulder.parcplace.com (Warner Losh)
Date: Fri, 22 Jul 1994 18:25:45 GMT Raw View
The following code fails to compile with Microsoft's compiler. It
compiles w/o a hitch with cfront, Lucid's lcc, IBM's xlC, DEC's cxx
and g++ (2.4.x and 2.6.0). I maintain that the code is legal, but I
can't seem to find in the ARM or other docs where this is stated.
class B { public: B(); virtual ~B(); };
typedef void *(B::*memfnp)();
class D : public B { public: D() ; virtual ~D();
char *fn();
};
memfnp m = (memfnp) &D::fn; // Line 6
I feel that Line 6 is correct, because the return values are
compatible and the rest fo fn is cool. Microsoft's compiler, however,
complains:
"cast to pointer to member myst be from related pointer to member"
Comments?
Warner
--
Warner Losh imp@boulder.parcplace.COM ParcPlace Boulder
"... but I can't promote you to "Prima Donna" unless you demonstrate a few
more serious personality disorders"
Author: colin@rafferty.com (Colin Owen Rafferty)
Date: Mon, 25 Jul 1994 02:24:55 GMT Raw View
In article <CtCtuy.99r@boulder.parcplace.com> imp@boulder.parcplace.com (Warner Losh) writes:
> I maintain that the code is legal, but I
> can't seem to find in the ARM or other docs where this is stated.
> class B { public: B(); virtual ~B(); };
> typedef void *(B::*memfnp)();
> class D : public B { public: D() ; virtual ~D();
> char *fn();
> };
> memfnp m = (memfnp) &D::fn; // Line 6
> I feel that Line 6 is correct, because the return values are
> compatible and the rest fo fn is cool. Microsoft's compiler, however,
> complains:
> "cast to pointer to member myst be from related pointer to member"
The problem with this (as I see it) is that you are implicitly trying to
allow a function of a derived class to manipulate an object of a base
class.
Say you had the following code afterwards:
B b;
b.*m();
What if D::fn() tried to access member variables that are only part of a D?
This would cause a real headache at runtime.
Although I don't have my ARM with me, I would be pretty surprised if it
said that you could do this.
On the other hand, it would be perfectly valid to cast some B::fn() into
a member function pointer variable for class D.
I guess it can be summed up by saying that since objects of class D can
be treated as objects of class B (but not vice versa), the functions
acting on class B can act on class D (and not vice versa).
Of course, I could be totally wrong, and Microsoft could be cryptically
complaining about the fact that the typedef wants the member function to
return void, while D:fn returns char*.
--
<a href="http://rafferty.com/~colin/">Colin Rafferty</A>