Topic: Pointer to members
Author: Peter Jacobi <peter_jacobi@gmx.net>
Date: 2000/01/19 Raw View
Hi Ken, all,
Thank you for reassuring me, that I'm not totally off.
Nevertheless I have the desire to learn what the Standard
(and other compilers) says on this issue.
See below my revised example program, which now uses "manual
thunking" to avoid the casts. This scores 9.5 of 10 on the
ugliness scale, but as the code is to be generated
programmatically (from ASN.1 source) this doesn't matter
that much.
> Hmm. I see your point now. Sorry for taking insufficient care when
> reading your original post. Unfortunately, I agree with you.
> "Peter Jacobi" <peter_jacobi@gmx.net> wrote in message
> news:3875EC4D.4B31500F@gmx.net...
>>
>> struct B { int i; };
>> struct D: public B { int j; };
>>
>> class X {
>> B b;
>> D d;
>> };
>>
>> B X::*p1 = &X::b; // legal
>> D X::*p2 = &X::d; // legal
>>
>> B X::*p3 = &X::d; // illegal?
> [...]
>
>I see no way to subvert type safety here, so I think the code should
> be allowed.
New example program:
#include <iostream.h>
class A {
public:
int a;
};
class B: public A {
public:
int b;
};
class X {
public:
A a1;
A a2;
B b1;
B b2;
// "manual thunking"
A &geta1 () {return a1;}
A &geta2 () {return a2;}
A &getb1 () {return b1;}
A &getb2 () {return b2;}
};
int main () {
X x;
x.a1.a = 100;
x.a2.a = 200;
x.b1.a = 300;
x.b1.b = 301;
x.b2.a = 400;
x.b2.b = 401;
// new try, using "manual thunking"
// compiles and works and should be Standard C++
typedef A & (X::*MemberPointerThunk) ();
MemberPointerThunk mpta1 = &X::geta1;
MemberPointerThunk mpta2 = &X::geta2;
MemberPointerThunk mptb1 = &X::getb1;
MemberPointerThunk mptb2 = &X::getb2;
cout << ((x.*mpta1) ()).a << endl;
cout << ((x.*mpta2) ()).a << endl;
cout << ((x.*mptb1) ()).a << endl;
cout << ((x.*mptb2) ()).a << endl;
// old tries
typedef A X::*MemberPointer;
MemberPointer mpa1 = &X::a1;
MemberPointer mpa2 = &X::a2;
// compiles and works, but can I rely on that?
MemberPointer mpb1 = (MemberPointer) &X::b1;
MemberPointer mpb2 = (MemberPointer) &X::b2;
// doesn't compile
// MemberPointer mpb1 = &X::b1;
// MemberPointer mpb2 = &X::b2;
cout << (x.*mpa1).a << endl;
cout << (x.*mpa2).a << endl;
cout << (x.*mpb1).a << endl;
cout << (x.*mpb2).a << endl;
return 0;
}
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: bill@gibbons.org (Bill Gibbons)
Date: 2000/01/15 Raw View
In article <newscache$26vwnf$ql7$1@firewall.thermoteknix.co.uk>, "Ken
Hagan" <K.Hagan@thermoteknix.co.uk> wrote:
> "Peter Jacobi" <peter_jacobi@gmx.net> wrote in message
> news:38732F91.2C9ABD29@gmx.net...
> > Can pointers-to-A-members-of-X used to hold pointer-to-B-members-of-X if
> > B is derived from A?
>
> Consider
>
> struct B { int i; } b;
> struct D : B { int i; int j; } d; // (typo fixed)
>
> D* pd = &b; // illegal
> cout << pd->j << endl;
>
> int B::*p = &D::j; // illegal
> cout << b.*p << endl; // *** (1) ***
>
> Both assignments must be illegal since the lines following them
> attempt to reference a data member that does not exist in the
> base class object. You are presumably happy with the first case.
> The second obeys apparently opposite rules but the underlying
> reason is in fact the same.
>
> > b) Will the code with casts works on any standard comforming
> > compiler?
>
> No. A static_cast<> will silence the compiler but your code will
> still explode (in theory) when you dereference the pointers.
>> In practice, you might well get away with it if you take a pointer
> to the base class member "i".
>
> D* pd = static_cast<D*>(&b);
> cout << pd->i << endl;
>
> int B::*p = static_cast<int B::*>(&D::i);
> cout << b.*p << endl; // *** (2) ***
Not just in practice. This is specifically allowed by the standard.
You can cast a pointer to member to a different (non-virtaully-derived)
base or derived class as long as the target class is a base or derived
class of the one containing the original member. And you can dereference
the pointer to member with any object that actually contains the
specified member.
In (1) above, "b" does not contain a member "D::j". But in (2) above,
"*b" does contain a member "D::i" (when you consider its dynamic type),
and so the dereference is OK.
In article <3874ACD0.8FD3ABCD@sensor.com>, Ron Natalie <ron@sensor.com> wrote:
> Peter Jacobi wrote:
> >
> > Can pointers-to-A-members-of-X used to hold pointer-to-B-members-of-X if
> > B is derived from A?
>
> No. Because a member of B is not necessarily a member of A.
Wrong. See above and C++ FDIS 5.2.9 paragraph 9.
Also, you can cast (via old-style or reinterpret_cast) a pointer to member
to a PM of an unrelated class as long as you cast it back before you use
it. This is the same rule as with pointers to functions and (for data)
pointers
to void. See FDIS 5.2.10 paragraph 9.
This means you can have a "generic" pointer to member function and a "generic"
pointer to data member, much like "void (*)()" (by convention) and "void *"
for non-members.
> In practice, you'll find that pointer-to-member may not even be a fixed
> size. For example in VC++:
In practice, the VC++ implementation of pointers to members is broken.
-- Bill Gibbons
bill@gibbons.org
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Ken Hagan" <K.Hagan@thermoteknix.co.uk>
Date: 2000/01/15 Raw View
Hmm. I see your point now. Sorry for taking insufficient care when
reading your original post. Unfortunately, I agree with you.
"Peter Jacobi" <peter_jacobi@gmx.net> wrote in message
news:3875EC4D.4B31500F@gmx.net...
>
> struct B { int i; };
> struct D: public B { int j; };
>
> class X {
> B b;
> D d;
> };
>
> B X::*p1 = &X::b; // legal
> D X::*p2 = &X::d; // legal
>
> B X::*p3 = &X::d; // illegal?
&X::d is a pointer to a D member. The offset of the B base class
within that D member could be added to the offset of the D within
the X to yield an offset from the X to a B sub-object, which is certain
to exist (since X has no base or derived classes in this example).
This is not the usual "pointer-to-member/base-class" scenario.
The derived to base class conversion is being applied to an object,
not a pointer to member.
I see no way to subvert type safety here, so I think the code should
be allowed.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Peter Jacobi <peter_jacobi@gmx.net>
Date: 2000/01/06 Raw View
Can pointers-to-A-members-of-X used to hold pointer-to-B-members-of-X if
B is derived from A?
(See complete example code below for a more clear description)
The code doesn't compile with Watcom C++ 11.0 pr MSVC 6 without casting,
but compiles and works with casting.
My questions are:
a) Why are casts required?
b) Will the code with casts works on any standard comforming compiler?
Regards,
Peter
Sample program:
#include <iostream.h>
class A {
public:
int a;
};
class B: public A {
public:
int b;
};
class X {
public:
A a1;
A a2;
B b1;
B b2;
};
int main () {
X x;
x.a1.a = 100;
x.a2.a = 200;
x.b1.a = 300;
x.b1.b = 301;
x.b2.a = 400;
x.b2.b = 401;
typedef A X::*MemberPointer;
MemberPointer mpa1 = &X::a1;
MemberPointer mpa2 = &X::a2;
// compiles and works, but can I rely on that
MemberPointer mpb1 = (MemberPointer) &X::b1;
MemberPointer mpb2 = (MemberPointer) &X::b2;
// doesn't compile
// MemberPointer mpb1 = &X::b1;
// MemberPointer mpb2 = &X::b2;
cout << (x.*mpa1).a << endl;
cout << (x.*mpa2).a << endl;
cout << (x.*mpb1).a << endl;
cout << (x.*mpb2).a << endl;
return 0;
}
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Ken Hagan" <K.Hagan@thermoteknix.co.uk>
Date: 2000/01/06 Raw View
"Peter Jacobi" <peter_jacobi@gmx.net> wrote in message
news:38732F91.2C9ABD29@gmx.net...
> Can pointers-to-A-members-of-X used to hold pointer-to-B-members-of-X if
> B is derived from A?
>
> The code doesn't compile with Watcom C++ 11.0 pr MSVC 6 without casting,
> but compiles and works with casting.
>
The compilers are correct. Consider
struct B { int i; } b;
struct D { int i; int j; } d;
D* pd = &b; // illegal
cout << pd->j << endl;
int B::*p = &D::j; // illegal
cout << b.*p << endl;
>
> a) Why are casts required?
>
Both assignments must be illegal since the lines following them
attempt to reference a data member that does not exist in the
base class object. You are presumably happy with the first case.
The second obeys apparently opposite rules but the underlying
reason is in fact the same.
>
> b) Will the code with casts works on any standard comforming
> compiler?
>
No. A static_cast<> will silence the compiler but your code will
still explode (in theory) when you dereference the pointers.
In practice, you might well get away with it if you take a pointer
to the base class member "i".
D* pd = static_cast<D*>(&b);
cout << pd->i << endl;
int B::*p = static_cast<int B::*>(&D::i);
cout << b.*p << endl;
This kind of code has its place. The vector template class may
be specialised for pointers, with vector<T*> being partially
specialised as a type-safe wrapper around vector<void*>.
Application frameworks often take pointers to your derived class
objects and return them to you later on as pointers to base class
objects. In both cases we know what the object is, so static_cast
is the most efficient way to "recover" the type information.
If we don't know (can't prove) what the object is, then the only
safe option is a dynamic_cast.
D* pd = dynamic_cast<D*>(&b);
if (pd)
cout << pd->i << endl;
int B::*p = dynamic_cast<int B::*>(&D::i);
if (p)
cout << b.*p << endl;
Hmm... I've never seen dynamic_cast on a pointer-to-member.
Is it legal?
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Ron Natalie <ron@sensor.com>
Date: 2000/01/06 Raw View
Peter Jacobi wrote:
>
> Can pointers-to-A-members-of-X used to hold pointer-to-B-members-of-X if
> B is derived from A?
No. Because a member of B is not necessarily a member of A. You can go
the other way, because a member of A is always by definition of member of
B.
In practice, you'll find that pointer-to-member may not even be a fixed
size. For example in VC++:
class A {
...
};
class B {
...
};
class C : public A, public B {
...
};
that sizeof some A::* would be 4, while sizeof some B::* is 8.
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Peter Jacobi <peter_jacobi@gmx.net>
Date: 2000/01/07 Raw View
Ken, all,
Sorry you are answering the wrong question. I cannot clearly phrase
my question in prose, but please look at the original sample code.
I also try below to contrast your sample with the case I meant.
Ken Hagan wrote:
> The compilers are correct. Consider
> struct B { int i; } b;
> struct D { int i; int j; } d;
> D* pd = &b; // illegal
> cout << pd->j << endl;
> int B::*p = &D::j; // illegal
> cout << b.*p << endl;
> Both assignments must be illegal since the lines following them
> attempt to reference a data member that does not exist in the
> base class object.
I meant:
struct B { int i; };
struct D: public B { int j; };
class X {
B b;
D d;
};
B X::*p1 = &X::b; // legal
D X::*p2 = &X::d; // legal
B X::*p3 = &X::d; // illegal?
typedef B X::*PXB;
B X::*p4 = (PXB) p&X::d; // compiles, but will it produce always the
intended result
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html ]