Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why no implicit member pointer conversion?

5 views
Skip to first unread message

Bjarke Dahl Ebert

unread,
Jan 11, 1999, 3:00:00 AM1/11/99
to
I'm wondering why there's no such thing as implicit conversion of
pointer to
data member.

In the following code

=======================================
class Base { };

class Derived { };

class Foo
{
public:
Base a;
Derived b;
};

Base Foo::* memptr = &Foo::b;
=======================================

the last line fails to compile, because &Foo:b has the type "Derived
Foo::*".

I guess that an explicit cast would do what I'm looking for ("(Base
Foo::*)&Foo::b" compiles), but why is there no implicit conversion as
there is
for ordinary pointers?

Am I right to assume that a member pointer is (almost always)
implemented as an
index into the value representation of an object of the class? Then an
implicit
conversion could be done by adding a constant to this index, just as
an
implementation adds 0 or more to an object pointer to obtain a pointer
to one of
its non-virtual base classes.


Regards,
Bjarke Ebert

--
Systems Engineer Cryptomathic A/S
Bjarke Dahl Ebert Klostergade 28
DK-8000 Århus C
Denmark

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Bruce DeVisser

unread,
Jan 11, 1999, 3:00:00 AM1/11/99
to
On 11 Jan 1999 08:18:33 -0500, Bjarke Dahl Ebert wrote:
> =======================================
> class Base { };

> class Derived { };

> class Foo
> {
> public:
> Base a;
> Derived b;
> };

> Base Foo::* memptr = &Foo::b;
> =======================================

> the last line fails to compile, because &Foo:b has the type "Derived
> Foo::*".

> I guess that an explicit cast would do what I'm looking for ("(Base
> Foo::*)&Foo::b" compiles), but why is there no implicit conversion as
> there is for ordinary pointers?

Because Derived is unrelated to Base. :)

Try s/class Derived/class Derived : public Base/

Bruce DeVisser
b...@torcon.com

Alex Martelli

unread,
Jan 12, 1999, 3:00:00 AM1/12/99
to
Bruce DeVisser wrote in message
[snip]

>> I guess that an explicit cast would do what I'm looking for ("(Base
>> Foo::*)&Foo::b" compiles), but why is there no implicit conversion as
>> there is for ordinary pointers?
>
>Because Derived is unrelated to Base. :)
>
>Try s/class Derived/class Derived : public Base/


That was my first thought, too, but, after doing
that substitution, _nothing changes_. Compiler
error, or, anomaly in the Standard...?


Alex

-----------== Posted via Newsfeeds.Com, Uncensored Usenet News ==----------
http://www.newsfeeds.com/ The Largest Usenet Servers in the World!
-----------== Over 66,000 Groups, Plus a Dedicated Binaries Server
==----------

Bjarke Dahl Ebert

unread,
Jan 12, 1999, 3:00:00 AM1/12/99
to
Bruce DeVisser wrote:

> On 11 Jan 1999 08:18:33 -0500, Bjarke Dahl Ebert wrote:
> > =======================================
> > class Base { };
>
> > class Derived { };
> > [ ...]

> > the last line fails to compile, because &Foo:b has the type "Derived
> > Foo::*".

> Because Derived is unrelated to Base. :)


>
> Try s/class Derived/class Derived : public Base/


Oh, damn... A small typo :). But the "correct" code also fails to
compile in
Borland C++Builder 3.
It says:
[C++Error] Test.cpp(27): Cannot convert 'Derived Foo::*' to 'Base
Foo::*'.
(and here, Derived *is* a derived class of Base).

Is the compiler wrong or is the standard too restrictive, or neither?

Bjarke Ebert

Horst Kraemer

unread,
Jan 12, 1999, 3:00:00 AM1/12/99
to
On 11 Jan 1999 08:18:33 -0500, Bjarke Dahl Ebert <b...@cryptomathic.dk>
wrote:

> I'm wondering why there's no such thing as implicit conversion of
> pointer to
> data member.
>
> In the following code
>

> =======================================
> class Base { };
>
> class Derived { };
>

> class Foo
> {
> public:
> Base a;
> Derived b;
> };
>
> Base Foo::* memptr = &Foo::b;
> =======================================
>

> the last line fails to compile, because &Foo:b has the type "Derived
> Foo::*".


Well, the name 'Derived' doesn't make class Derived automatically a
class derived from 'Base'....

Replace

class Derived {};

by

class Derived : public Base {};


and it will compile.

Regards
Horst

Bruce DeVisser

unread,
Jan 12, 1999, 3:00:00 AM1/12/99
to
On 12 Jan 1999 07:56:46 -0500, Alex Martelli wrote:
> Bruce DeVisser wrote in message
> >Try s/class Derived/class Derived : public Base/

> That was my first thought, too, but, after doing


> that substitution, _nothing changes_. Compiler
> error, or, anomaly in the Standard...?


[main:~/bin]$ g++ --version
egcs-2.90.27 980315 (egcs-1.0.2 release)
[main:~/bin]$ cat test.cpp
class Base { };

#ifdef FIX
class Derived : Base { };
#else
class Derived { };
#endif

class Foo{
public:
Base a;
Derived b;
};

int main()
{


Base Foo::* memptr = &Foo::b;

return 0;
}
[main:~/bin]$ g++ -Wall -ansi -pedantic test.cpp
test.cpp: In function `int main()':
test.cpp:17: initialization to `Base Foo::*' from `Derived Foo::*'
test.cpp:17: warning: unused variable `class Base Foo::* memptr'
[main:~/bin]$ g++ -DFIX -Wall -ansi -pdedantic test.cpp
test.cpp: In function `int main()':
test.cpp:17: warning: unused variable `class Base Foo::* memptr'


As you can see, I still get the obvious warning, but the error itself
disappears when Derived is actually Derived from Base. My guess would
be a compiler error, but then I'm far from being an expert on the
standard.

Bruce DeVisser
b...@torcon.com

John W. Schwegler

unread,
Jan 12, 1999, 3:00:00 AM1/12/99
to
Alex Martelli (al...@magenta.com) wrote:
: Bruce DeVisser wrote in message
: [snip]

: >> I guess that an explicit cast would do what I'm looking for ("(Base
: >> Foo::*)&Foo::b" compiles), but why is there no implicit conversion as
: >> there is for ordinary pointers?
: >
: >Because Derived is unrelated to Base. :)
: >
: >Try s/class Derived/class Derived : public Base/


: That was my first thought, too, but, after doing
: that substitution, _nothing changes_. Compiler
: error, or, anomaly in the Standard...?


Once I made the substitution, I had no problems compiling this
using the egcs g++ compiler:

gcc version egcs-2.90.27 980315 (egcs-1.0.2 release)


-John
--
______________________________________________________________________________
John Schwegler - replies to: jo...@flower.aud.temple.edu

Hofstadter's Law: It always takes longer than you expect,
even when you take Hofstadter's Law into account.

Thiemo Seufer

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
Trying the FIXED code version with VC 5.0 SP3, I got the error:

error C2440: 'initializing' : cannot convert from 'class Derived Foo::*' to
'class Base Foo::*' Types pointed to are unrelated; conversion requires
reinterpret_cast, C-style cast or function-style cast

I believe the compiler messages are misleading. I think the problem is
that 'memptr' tries to take the memory address of Foo::b, but this
is the name of a non-static class member and NOT referring to
an instance, so there is no memory address.

What do You expect 'memptr' pointing to?

Thiemo Seufer

Ian McCulloch

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to

Bjarke Dahl Ebert wrote in message <369B0DB2...@cryptomathic.dk>...
>Bruce DeVisser wrote:
>

[snip]

>Borland C++Builder 3.
>It says:
>[C++Error] Test.cpp(27): Cannot convert 'Derived Foo::*' to 'Base
>Foo::*'.
>(and here, Derived *is* a derived class of Base).
>
>Is the compiler wrong or is the standard too restrictive, or neither?


I think the problem is you can only cast member pointers from the base class
to the derived class, not the other way around. This makes sense: if a
member is defined in the base, then it automatically is a property of the
derived class also, so you can cast from the base member pointer to the
derived member pointer. But the other way around does not follow.

Cheers,
Ian McCulloch

Bjarke Dahl Ebert

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
Thiemo Seufer wrote:

> I believe the compiler messages are misleading. I think the problem is
> that 'memptr' tries to take the memory address of Foo::b, but this
> is the name of a non-static class member and NOT referring to
> an instance, so there is no memory address.

The expression &Foo::b is an rvalue of type "MEMBER pointer to Derived",
alias "Derived Foo::*". A member pointer is totally different from an
ordinary
pointer. Nobody would expect &Foo::b to be an address. (Rather an index into
objects of class Foo).

> What do You expect 'memptr' pointing to?

Nothing, until you present it to an expression, <expr>, of type Foo or a
class
derived from Foo.
Then "<expr> .* memptr" designates the b data member of <expr>, as an rvalue
or
lvalue, respectively.


--
Systems Engineer Cryptomathic A/S
Bjarke Dahl Ebert Klostergade 28
DK-8000 Århus C

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bjarke Dahl Ebert

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
Ian McCulloch wrote:

> I think the problem is you can only cast member pointers from the base
class
> to the derived class, not the other way around. This makes sense: if a
> member is defined in the base, then it automatically is a property of the
> derived class also, so you can cast from the base member pointer to the
> derived member pointer. But the other way around does not follow.

I think you're thinking about the situation where you convert "Base::* Foo"
to
"Derived::* Foo", and in this situation, I agree.

But the issue here is a conversion from type "Foo::* Derived" to "Foo::*
Base"!

Regards,
Bjarke

Hiram Berry

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
Bjarke Dahl Ebert wrote:

> Ian McCulloch wrote:
>
> > I think the problem is you can only cast member pointers from the base
> class
> > to the derived class, not the other way around. This makes sense: if a
> > member is defined in the base, then it automatically is a property of the
> > derived class also, so you can cast from the base member pointer to the
> > derived member pointer. But the other way around does not follow.
>
> I think you're thinking about the situation where you convert "Base::* Foo"
> to
> "Derived::* Foo", and in this situation, I agree.

Yes, standard conversion is supported from the base to the derived class
pointer to member, so you can do implicit conversions using it, but not from
derived to base. It's a reasonable restriction since derived classes can have
members that don't exist in the base class. However, it is OK to *explicitly*
cast from derived to base using static_cast, as in the following:

struct Base{ };
struct Derived:public Base{
void fxn( );
};
void (Base::*fptr)( ) = static_cast<void (Base::*)( )>(&Derived::fxn);

This lets you use derived class specific objects or functions to be used as
arguments to functions that take base class pointers. Of course the base class
pointers must be of derived class dynamic type in order to be used properly;
requiring the explicit cast is a way to remind the programmer of this fact.

> But the issue here is a conversion from type "Foo::* Derived" to "Foo::*
> Base"!

I don't think that is allowed; the operations allowed on pointers to member are
very restricted. I did not see that type of conversion listed in the
enumerations of allowed conversions for the different kinds of new-style casts
or the standard conversions. It makes some sense, though-- if a class has a
Derived member, that's not really the same as having a Base member. Even
though the Derived object does contain a Base object, the Derived class may
have modified the visibility of the Base object's members; it no longer behaves
the same way as a Base member would, so it shouldn't be accessed through the
pointer-to-member mechanism as if it were one.

OTOH, there is a way to provide a common handle for using both the Base and
Derived members of the containing class: declare pointers to the base class
within the containing class and use member-pointers to pointers to Base outside
it. For example:

struct Base{ };
struct Derived{ };
struct Container{
Base b;
Derived d;
Base *bptr,*dptr;
Container( ):bptr(&b),dptr(&d){}
};

Now you can do:

Base *Container::*memptr = &Container::dptr;

outside the class, giving you an equivalent representation for all members,
both base and derived.

-- Hiram Berry

John Potter

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
Bjarke Dahl Ebert <b...@cryptomathic.dk> wrote:

: The expression &Foo::b is an rvalue of type "MEMBER pointer to


Derived",
: alias "Derived Foo::*". A member pointer is totally different from an
: ordinary pointer. Nobody would expect &Foo::b to be an address.
(Rather
: an index into objects of class Foo).

You have done a nice job of refuting the wrong answers and right answers
to the wrong question. Your shout above is the key to everything. Let
me restate the problem including all the other questions you are not
asking.

struct B { int b; };
struct D : B { int d; };
struct F { B fb; D fd; };

B b;
D d;
F f;
// pointer to object
B* bp(&d); // d is-a B std conv
int x(bp->b);
D* dp(static_cast<D*>(&b)); // inverse of above
// pointer to member
int D::* idp(&B::b); // D has-a B (B::b == D::b) std conv
int y(d.*idp);
int B::* ibp(static_cast<int B::*>(&D::d)); // inverse of above
// Now the point
// pointer to member
B F::* fbp(&F::fd); // Builder rejects, egcs accepts
int z((f.*fbp).b);
B F::* fbq(static_cast<B F::*>(&F::fd)); // egcs rejects
D F::* fdp(static_cast<D F::*>(&F::fb)); // egcs rejects

The questionable line is obviously not a standard conversion since the
two following rejected static_casts would be valid if it were. The egcs
bug has only helped to cloud the issue.

There is no standard conversion in chapter 4 for T ::C* to U ::C* for
any form of T and U. That answers the subject question, but I think you
knew that.

Consider:

int F::* fip(&(b in fb));

I can't even express it. Is that int member of fb really much different
from the B member of fd when considered as a member of F? Of course,
reinterpret_cast<int F::*>(&F::fb) will work.

Hopefully someone knowledgable will give a better answer to the question
of why the conversion you want is not listed in chapter 4. If nothing
happens, reword the question and post to comp.std.c++.

John

Hiram Berry

unread,
Jan 15, 1999, 3:00:00 AM1/15/99
to
Bjarke Dahl Ebert wrote:

Now you can do:

-- Hiram Berry

John Potter

unread,
Jan 15, 1999, 3:00:00 AM1/15/99
to

Consider:

John

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Valentin Bonnard

unread,
Jan 17, 1999, 3:00:00 AM1/17/99
to

John Potter wrote:

> struct B { int b; };
> struct D : B { int d; };
> struct F { B fb; D fd; };
>
> B b;
> D d;
> F f;

> B F::* fbp(&F::fd); // Builder rejects, egcs accepts

> Hopefully someone knowledgable will give a better answer to the question

not me !

> of why the conversion you want is not listed in chapter 4. If nothing
> happens, reword the question and post to comp.std.c++.

This conversion is at the same time type safe and
implementable: a pointer to member is an offset (and
perhaps other informations), so you just have to apply
the delta between B and D.

Also the cast in the reversed direction should be allowed
as a static_cast<D F::*> (B F::*).

I have never understoud the reasons (if there are any)
why it isn't allowed.

--

Valentin Bonnard mailto:bonn...@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]


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


Bjarke Dahl Ebert

unread,
Jan 20, 1999, 3:00:00 AM1/20/99
to
John Potter wrote:
>

> You have done a nice job of refuting the wrong answers and right answers
> to the wrong question. Your shout above is the key to everything. Let
> me restate the problem including all the other questions you are not
> asking.

> [...]

Thank you for your detailed summary of the discussion.

Yes, my first question was actually "Why not supported", but then I got in
doubt
when someone reported that egcs accepts the code (which now seems to be an
egcs
bug).

So, I think the conclusion of this issue is that

1) The conversion in question is not a standard conversion
2) Nobody knows why C++ is restricted in this way.

:-)

I think that this language restriction destroys the notion of "is-a"
relationship. If D is-a B, then I _should_ be able to point to a B member of
Foo, that is by accident also a D.

Regards,
Bjarke Dahl Ebert

Hiram Berry

unread,
Jan 21, 1999, 3:00:00 AM1/21/99
to
Bjarke Dahl Ebert wrote:

> So, I think the conclusion of this issue is that
>
>  1) The conversion in question is not a standard conversion
>  2) Nobody knows why C++ is restricted in this way.
>
> :-)

I think this limitation is overly restrictive.  Your proposed extension
would be a useful tool in using pointers to member. There are also other,
similar restrictions that hinder the effectiveness of the p.t.m.
mechanism. For example, unlike nonmember pointers, there is no conversion
from member array of type to pointer to member to type, and you can't do
pointer arithmetic on them, even implicitly as a array subscript. Eg:

struct foo{ int x[5],y;} bar;
void fxn(foo &,int foo::*);
fxn(bar,&foo::y); // is OK, but
fxn(bar,&foo::(x[3])); //this,and anything similar, is an error

This restriction is just about as frustrating and needless as the one you're
talking about. It cripples our ability to use p.t.m.'s to operate on member
arrays. I think there is a simple solution: let member arrays convert to
pointers to member type, implement the syntax used in the last line
above, and allow operator* to be applied to member functions, the return
value being a member pointer to what would be generated by dereferencing
the "member_pointed to" part. Ie:

int *foo::*fmaptr=&foo:x;
fxn(bar,x[3]); //OR
fxn(bar,*(x+3)); //neither of which is currently legal

> I think that this language restriction destroys the notion of "is-a"
> relationship.

It does.

> If D is-a B, then I _should_ be able to point to a B member of
> Foo, that is by accident also a D.

You can, but just not with an intuitive cast of a pointer to member.  You can
either (1) declare pointers to member to ( pointers to || references to) base
class B inside the container, and use these ptm's, or (2) do the static_cast
back to B *  only after dereferencing. Having your cast would make things a lot
more straightforward, though.

I can think of a few (weak) objections that might be raised against this kind of
cast.  First, member pointers do not currently reference base classes in any
usage; with:

Derived:Base{/*...*/};

there is no possible way to declare a Base Derived::* to the Base object of a
Derived object.  Your cast would allow just this sort of access.  Second, I
don't think implementations are required to physically order or pack base
classes in memory in any specified way; conceivably the order might vary even
between different objects of the same class.  As a result, the p.t.m. generated
from your cast cannot portably be represented as a simple offset into a class,
which could add a significant performance penalty onto using pointers to
member.  And the last objection I can think of is the one I mentioned before,
that an aggregate containing a Derived doesn't in all respects conceptually
contain a Base ( and the proper answer to that objection is to bring up the
"is_a" relationship, like you did).

I believe the utility of allowing this type of cast outweighs these objections,
though.

-- Hiram Berry

Hiram Berry

unread,
Jan 21, 1999, 3:00:00 AM1/21/99
to
I wrote:

> fxn(bar,x[3]); //OR

should have said "fxn(bar,fmaptr[3]);"

> fxn(bar,*(x+3)); //neither of which is currently legal

should have said "fxn(bar,*(fmaptr+3));"

My apologies for any confusion,

Hiram Berry.

cs_...@stu.ust.hk

unread,
Jan 23, 1999, 3:00:00 AM1/23/99
to
Hiram Berry <burn...@burningbridges.com> wrote:
> I think this limitation is overly restrictive.  Your proposed extension
> would be a useful tool in using pointers to member. There are also other,
> similar restrictions that hinder the effectiveness of the p.t.m.
> mechanism. For example, unlike nonmember pointers, there is no conversion
> from member array of type to pointer to member to type, and you can't do
> pointer arithmetic on them, even implicitly as a array subscript. Eg:

> This restriction is just about as frustrating and needless as the one you're


> talking about. It cripples our ability to use p.t.m.'s to operate on member
> arrays. I think there is a simple solution: let member arrays convert to
> pointers to member type, implement the syntax used in the last line
> above, and allow operator* to be applied to member functions, the return
> value being a member pointer to what would be generated by dereferencing
> the "member_pointed to" part. Ie:

[examples and others snipped]
I would tend to think one could get a faster implementation
of pointer to members if array to pointer conversions aren't
allowed. It seems to me that, while usual pointers store some
address to the object, pointer to members store an offset of
the member in the class.
An array to pointer conversion would require the address of the
start of array be known, taken out, and stored in a pointer.
But it is impossible to find an address that stores the address
of the start of a member array(i.e. pointer to pointer to member,
which gives you the conversion) without extra cost.
But then, you can still apply the pointer to member array to
an object and apply the pointer conversion from there.

Would a solution (for member array => member pointer) to use
pointer to member function instead? The function would perform
the decay for you. It would be slow as it can't be inlined, though.

Just my $0.2.

0 new messages