I hope someone can help me since can't quite understand how it is possible that the following code can work.
I have a base class with some classes that derive from it. Each derived class has its own non-virtual function not known by the others.
I use a vector to store objects and member functions pointers of derived classes (casting them to member functions of base class, a trick to have a single vector) and call them whenever I need.
Question, how is it possible that in the callback number 3, I can call the function of derived class B using an object of type A?
In the callback number 4, I tried something even more extreme, but maybe I just got lucky with the compiler...
#include <iostream>
#include <vector>
using namespace std;
class Base {
public:
Base() {}
};
class DerivedA : public Base {
public:
DerivedA() {}
void CallbackA() { cout << "Callback A" << endl; }
};
class DerivedB : public Base {
public:
DerivedB(){}
void CallbackB() { cout << "Callback B" << endl; }
};
class AnotherBase {
public:
AnotherBase() {}
};
class DerivedC : public AnotherBase {
public:
DerivedC() {}
void CallbackC() { cout << "Callback C" << endl; }
};
typedef void (Base::* BaseMemFn)();
struct Callback {
Callback( Base * base, BaseMemFn memFn ) : object(base), function(memFn) {}
Base* object;
BaseMemFn function;
};
int main() {
DerivedA derived_a;
DerivedB derived_b;
DerivedC derived_c;
vector<Callback> callbacks;
callbacks.push_back(Callback(&derived_a, static_cast<void (Base::*)()>(&DerivedA::CallbackA) ) ); // Callback 1: Ok
callbacks.push_back(Callback(&derived_b, static_cast<void (Base::*)()>( &DerivedB::CallbackB ) ) ); // Callback 2: Ok
callbacks.push_back(Callback(&derived_a, static_cast<void (Base::*)()>(&DerivedB::CallbackB))); // Callback 3: Derived A can call a function of derived B ?
callbacks.push_back(Callback(reinterpret_cast<Base *>(& derived_c), reinterpret_cast<void (Base::*)()>(&DerivedC::CallbackC))); // Callback 4: Luck ?
for (Callback& callback : callbacks) {
(callback.object->*callback.function)();
}
}
The output:
Callback A
Callback B
Callback B
Callback C
Thank you
I believe Cases 1 and 2 are well-defined:
[expr.static.cast]/12 A prvalue of type "pointer to member of
D
of type cv1T
" can be converted to a prvalue of type "pointer to member ofB
of type cv2T
", whereD
is a complete class type andB
is a base class ofD
... If classB
... 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 behavior is undefined. [Note: Although classB
need not contain the original member, the dynamic type of the object with which indirection through the pointer to member is performed must contain the original member; see [expr.mptr.oper]. —end note][expr.mptr.oper]/4 Abbreviating pm-expression.*cast-expression as
E1.*E2
,E1
is called the object expression. If the dynamic type ofE1
does not contain the member to whichE2
refers, the behavior is undefined.
In other words, it's OK to cast from void (D::*)()
to void (B::*)()
. Moreover, it's OK to call the result of that cast on B*
pointer, as long as the actual most-derived object behind that pointer is D
or is derived from D
(and thus inherits the original D
's member).
Case 3, where you take an adress of DerivedB
's member and call it on DerivedA
instance, exhibits undefined behavior by violating [expr.mptr.oper]/4 above - calling a member through a pointer whose dynamic type (here, DerivedA
) doesn't contain the member (here, DerivedB::CallbackB
).
Case 4, the one with reinterpret_cast<Base*>(&derived_c)
, exhibits undefined behavior in this cast already (or to be precise, the cast itself is valid, but any attempt to use the resulting Base*
pointer is not).