Topic: memfun ptr and this
Author: bill@gibbons.org (Bill Gibbons)
Date: 1997/04/30 Raw View
In article <5jvqlo$k4o$1@jake.esu.edu>, jpotter@falcon.lhup.edu (John E.
Potter) wrote:
> In the following, a pointer to derived member function is stored in a
> pointer to base member function and invoked via this of a base member
> function. I expected the derived member function to be called with a
> bad this pointer. Three of my four compilers confirmed my suspicions.
> The fourth issued a warning about implicit constant overflow and
> properly adjusted the this pointer.
>
> #include <iostream.h>
> struct B1 {
> B1 (long v) : val(v) { }
> long val;
> };
> struct B2 {
> typedef void (B2::*M)();
> B2 (M p) : ptr(p) { }
> void foo () { (this->*ptr)(); } // The question
> M ptr;
> };
> struct D : B1, B2 {
> D () : B1(7), B2(static_cast<M>(&D::bar)) { }
> void bar () { cout << val << endl; }
> };
> int main() {
> D d;
> d.foo(); // output is 7?
> }
>
> Is the fourth compiler following the draft or is it just lucky that
> its undefined behavior happens to be nice while the others are not
> so nice. 5.2.9[expr.static.cast]/9 seems to allow the above. But
> 5.5[expr.mptr.oper]/3-4 are not as easy. The dynamic type of the
> object is correct; however, the types of both the this pointer and
> the pointer to member function on the "question" line are base.
>
> Yes, I did check to see that the this pointer was really being
> adjusted and it was not an accident of layout. Is this pointer
> adjustment required in the above?
Pointer adjustment is required. Three of the four compilers you
tried have serious bugs in this area. The fourth has a minor
bug since there is no constant overflow here.
The draft standard allows considerable flexibility when casting
pointers to members. The basic restrictions are:
* The static type of the pointer to member must always specify
the original class containing the member or a class which is
a base class of, or derived from, that class.
If the two static types are unrelated, the error is caught
at compile time. Otherwise (such as casting through a
derived class and back to a different branch of the hierarchy)
the behavior is unspecified - which generally means it
compiles but doesn't work. Note that you cannot have this
situation without using an explicit (and therefore not
completely statically checkable) cast.
* When a pointer to member is dereferenced, the complete object
must contain the member.
Otherwise you get undefined behavior. This case generally
cannot be diagnosed at compile time. Note that the problem
cannot occur without an explicit cast.
* Pointers to members may not be cast across virtual inheritance.
This was allowed in earlier versions of the draft, but is
not allowed in the current version. Such a cast was
potentially expensive in space and/or time, and allowing such
casts required more stringent (and somewhat surprising)
restrictions on the uses of pointers to members.
In particular, the older drafts did not allow casting a
pointer to member to a new type in which the class did not
contain the member itself, as is done in your example.
Some compilers have not yet been updated to the latest requirements
for pointers to members, especially where multiple inheritance
is involved.
The usual problem is that a compiler assumes that if a pointer
to member's (static type) class does not use multiple inheritance,
then no offset adjustment need be done when dereferencing the
pointer to member. This was true under the old rules, but not
under the current rules.
-- 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/04/28 Raw View
In the following, a pointer to derived member function is stored in a
pointer to base member function and invoked via this of a base member
function. I expected the derived member function to be called with a
bad this pointer. Three of my four compilers confirmed my suspicions.
The fourth issued a warning about implicit constant overflow and
properly adjusted the this pointer.
#include <iostream.h>
struct B1 {
B1 (long v) : val(v) { }
long val;
};
struct B2 {
typedef void (B2::*M)();
B2 (M p) : ptr(p) { }
void foo () { (this->*ptr)(); } // The question
M ptr;
};
struct D : B1, B2 {
D () : B1(7), B2(static_cast<M>(&D::bar)) { }
void bar () { cout << val << endl; }
};
int main() {
D d;
d.foo(); // output is 7?
}
Is the fourth compiler following the draft or is it just lucky that
its undefined behavior happens to be nice while the others are not
so nice. 5.2.9[expr.static.cast]/9 seems to allow the above. But
5.5[expr.mptr.oper]/3-4 are not as easy. The dynamic type of the
object is correct; however, the types of both the this pointer and
the pointer to member function on the "question" line are base.
Yes, I did check to see that the this pointer was really being
adjusted and it was not an accident of layout. Is this pointer
adjustment required in the above?
John
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]