Since this question seems to be causing some contention, I am editing it to first show the intent with hypothetical syntax, and then show an implementation. The implementation relies on a surprising type cast followed by a call of this type casted pointer. The problem is that the type cast is standard (though non-portable) C++, but calling its result is undefined behavior. My question concerns whether the standard has lately or may soon change the result of calling the type casted member function pointer to no longer be undefined behavior.
The intent is to be able to write code like:
void* object = ...; universal_mf_ptr mf_ptr = ...;
Casting between member function pointers is an extremely murky area. During the standardization of C++, there was a lot of discussion about whether you should be able to cast a member function pointer from one class to a member function pointer of a base or derived class, and whether you could cast between unrelated classes. By the time the standards committee made up their mind, different compiler vendors had already made implementation decisions which had locked them into different answers to these questions. According to the Standard (section 5.2.10/9), you can use reinterpret_cast to store a member function for one class inside a member function pointer for an unrelated class. The result of invoking the casted member function is undefined. The only thing you can do with it is cast it back to the class that it came from. I'll discuss this at length later in the article, because it's an area where the Standard bears little resemblance to real compilers.
class TypeEraser; // Not a base of anything.
typedef void (TypeEraser::*erased_fptr)();
map<string, erased_fptr> functions;
// Casting & storage as if member function of unrelated class is in the standard
functions["MyFunc"] = reinterpret_cast<erased_fptr>(&MyClass::MyFunc);
TypeEraser* my_obj = (TypeEraser*)(void*)new MyClass;
erased_fpr my_func = functions["MyFunc"];
// !!! But calling it is undefined behavior according to standard !!!
No. The wording in in [expr.mptr.oper], as of N4606, reads:
The binary operator
->*binds its second operand, which shall be of type “pointer to member of
T” to its first operand, which shall be of type “pointer to
Tor a class of which
Tis an unambiguous and accessible base class.
In the example
void, which does not satisfy the conditions, so the code is simply ill-formed. I am not aware of any proposal to change this.
For the new verison of the code, where you now use
reinterpret_cast<TypeEraser*>(obj) instead so the types match... still no, as per [basic.lval]:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
(8.1) — the dynamic type of the object,
(8.2) — a cv-qualified version of the dynamic type of the object,
(8.3) — a type similar (as defined in 4.5) to the dynamic type of the object,
(8.4) — a type that is the signed or unsigned type corresponding to the dynamic type of the object,
(8.5) — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
(8.6) — an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
(8.7) — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
(8.8) — a
TypeEraser is none of those things for
MyClass, so it's undefined behavior.