In the beginning, there was C.
And C had structure, and expressions, and functions to package them. And it was good.
But C also had goto and switch case falling and syntax that followed use, so maybe not that good.
It also had pointers, causing much gnashing of teeth from aliasing and pointer arithmetic!
But it also had function pointers, allowing run time dispatch, and much rejoicing followed.
Now, data could dictate code, as well as code dictating data, and both were First Class (or close).
For anything that could be pointed to, could be pointed to, with the same pointer: the holy void*.
And all were equal in it's glory.
Then C++ came, and bound the data and the code into the Object.
And lo, this was just syntactical sugar, for a function and a method are not so different,
(no matter what the Sun or the Oracle may tell you).
Obj->Foo(int val) being (approximately) the same as Foo(Obj* this, int val), they were still equal,
under the holy void*.
And then, with inheritance, came strife, as the outer Derived class may add to the inner Base.
But lo, in these simple times, a solution was found: put every Base before Derived.
Then, with the same pointer, we may point to both child and father.
And still, anything that could be pointed to, could be pointed to with the holy void*.
With the Virtual, we lost our simplicity, and wandered for ages.
Wondering, how to deal with diamonds, or circles which are not quite ellipses.
But even as we cast off our old tenants of C, with each instruction reducing to simple asm,
we embraced, that some things should look simple (even if, under, they are complex).
And so, we looked at the secret VTables that arose and thought "good enough".
For while we had introduced hidden data, we had reduced complexity.
And now, any call made through a Virtual was redirected through a VTable.
And as all subobjects of a class could be pointed to through a single pointer, this was enough.
But even though the dispatch method had changed, we could still point to all things, with holy void*.
But then was made, what many in the present time, consider a grave mistake: Multiple Inheritance.
And no longer would one pointer suffice! For how can both Base fathers be at the start?
And no longer, then, would a VTable suffice, for how would we know which subobject to point to!
And now, the problem of diamonds arises even worse than before, with no obvious solution, and our prior ones requiring current code to deal with future prospects!
And so, pointer adjustments must be made, with each Virtual call.
Because a Base class may really be a MI Derived class in disguise, and needs to be adjusted.
So for the support of the Choice Few who used MI, we all paid a price.
And suddenly, the holy void* could no longer store what had hither-forth been simple sugar.
And what arose to deal with this complexity, was the dreaded member function pointer.
The beast required it's own syntax, for none others would suffice.
And this syntax, as rarely used, would be so low in precedence as to require parans with every use.
Though in the holy Standard, what wicked council decided to allow such wretched things to be Cast,
but when Cast from type to type, not Called without invoking behavior most undefined!
And nay, decadent with syntax and greedy, they were Fat and could not fit into void*.
As the only way to know what object to point to, was with an adjustment,
nestled deep in the pointer, and checked with each VTable lookup.
But this, brothers, is not how it must be.
This complexity of implementation comes from a most peculiar of decisions.
class Base1
{
public:
virtual void foo();
};
class Base2
{
public:
virtual void bar();
};
class Derived: public Base1, public Base2
{
public:
void unrelated();
}
As is seen here, Derived* must be adjusted when calling foo() or bar(); it cannot point to Base1 and Base2 at the same time, as in the case of simple single inheritance. It is, indeed, impossible to properly predict how much of an offset is needed, when called from a base class, which is why most have some sort of mechanism for adding it into the vtable.
However:
class Derived: public Base1, public Base2
{
public:
void unrelated();
virtual void foo() { Base1::foo(); }
virtual void bar() { Base2::bar(); }
}
Solves the problem, with nary a change to the original object model!
As each method now exists, it can be added properly to the vtable, and when called, knows exactly how much to adjust the pointer, allowing the call to proceed without any mucking about!
And now, both casting a member function pointer as well as calling it when cast, are well defined!
And most importantly of all, anything that can be pointed to, can be pointed to by a holy void*.
All a member function pointer need be is a normal function pointer that takes a special first parameter.
And lo, things would have been pretty good.
If only we lived in such a dream world.
Unfortunately for us, we live with fat member function pointers, vtables that have to adjust each and every call, and member function pointers that cannot appropriately create delegates or many other useful patterns. In MSVC, they change size when you cast them!
The problem is so great that std::function can allocate memory dynamically in most implementations, because of the various problems outlined here. The use of thunks for unoverriden methods, as I have detailed, would solve this problem quite handily, and the cost of this is a few inlinable hidden methods, and some vtable changes, along with a possible (but tiny) decrease in the speed of a virtual dispatch, in the case the function isn't overriden and also can't be inlined perfectly.
And for this tiny negligible increase in speed and space, we've created a monstrosity in member function pointers, and influenced a half dozen other languages not to use multiple inheritance despite the easily solved problems, castrated member function pointers in use, and made our delegates slower.
In fact, as noted in The Fastest Possible Delegates, the current solution actually slows down every Virtual invocation; it forces extra checking, and extra memory usage, via fat pointers, which even for single inheritance, must store extra data (or risk losing it, as MSVC member function pointers can). This is clearly not in the "pay if you use, not if you don't" philosophy of C++!
So, to reiterate, why are member function pointers different from "loose" function pointers? Is there any logical reason why they are not simply function pointers with a special calling convention, or with an extra argument for the "this"?