15

Let's say I have a pointer type that can hold the address of a base class method. Can I assign the address of a subclass method to it and expect it to work correctly? In my case I'm using it with a base class pointer and the dynamic type of the object is the derived class.

struct B
{
    typedef void (B::*MethodPtr)();
};

struct D: public B
{
    void foo() { cout<<"foo"<<endl; }
};

int main(int argc, char* argv[])
{
    D d;
    B* pb = &d;

    //is the following ok, or undefined behavior?
    B::MethodPtr mp = static_cast<B::MethodPtr>(&D::foo);
    (pb->*mp)();
}

The standard says this when talking about static_cast:

5.2.9.9 An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to member of B of type cv2 T”, where B is a base class (clause 10) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. 63) The null member pointer value (4.11) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined. [Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5.]

As always, I'm having such a hard time deciphering the standard. It kinda says that it is ok, but I'm not 100% sure if the above text really applies to the situation in my example code.

Timo
  • 5,125
  • 3
  • 23
  • 29
  • why can't you use the regular virtual function overriding? – YeenFei Nov 25 '10 at 01:36
  • coding is a bit like walking on a mountain road, walking in the middle of the road is safer than walking close to the brim, your code seem close to the brim. :-) – AndersK Nov 25 '10 at 03:07

1 Answers1

13

It's valid.

If class B contains the original member,

B doesn't contain D::Foo, so no.

or is a base [...] of the class containing the original member

B is a base of D, so this holds. As a result:

the resulting pointer to member points to the original member

Clause 5.2.9 9 says you can upcast only if you can also downcast, as specified in § 4.11:

An rvalue of type “pointer to member of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cv T,” where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed.

This just says you can downcast as long as B is accessible, isn't virtual and only appears once in D's inheritance diagram.

The danger inherent in upcasting method pointers is that you could call mp on an object whose actual type is B. As long as a code block that deals with D::* also deals with D*, you can avoid this.

outis
  • 75,655
  • 22
  • 151
  • 221
  • +1, but I think the last sentence is slightly misleading because having a `D*` is no guarantee that the pointed-to object's dynamic type is actually `D` (or some type derived from `D`), which is the property you actually need. – j_random_hacker Nov 29 '10 at 04:29
  • Yeah, I'm not too happy with the phrasing, but I'm having a hard time coming up with a rule that's sufficiently broad, correct and concise. Perhaps writing "introduces" rather than "deals"? – outis Dec 03 '10 at 23:43
  • How about `As long as a code block that deals with D::* also deals with a D* which points to an object whose dynamic type is either D or derived from D`? Might be overly verbose, though. – Justin Time - Reinstate Monica Jun 14 '16 at 20:08