I disagree with @sechastain
.
Inlining is far from being automatic. Whether or not the method is defined in place or a hint (inline
keyword or __forceinline
) is used, the compiler is the only one to decide if the inlining will actually take place, and uses complicated heuristics to do so. One particular case however, is that it shall not inline a call when a virtual method is invoked using runtime dispatch, precisely because runtime dispatch and inlining are not compatible.
To understand the precision of "using runtime dispatch":
IClassInterface* i = /**/;
i->LOL(); // runtime dispatch
i->QueueClass::LOL(); // compile time dispatch, inline is possible
@0xDEAD BEEF
: I find your design brittle to say the least.
The use of C-Style casts here is wrong:
QueueClass* p = /**/;
IClassInterface* q = p;
assert( ((void*)p) == ((void*)q) ); // may fire or not...
Fundamentally there is no guarantee that the 2 addresses are equal: it is implementation defined, and unlikely to resist change.
I you wish to be able to safely cast the void*
pointer to a IClassInterface*
pointer then you need to create it from a IClassInterface*
originally so that the C++ compiler may perform the correct pointer arithmetic depending on the layout of the objects.
Of course, I shall also underline than the use of global variables... you probably know it.
As for the reason of the absence ? I honestly don't see any apart from a bug in the compiler/linker. I've seen inlined definition of virtual
functions a few times (more specifically, the clone
method) and it never caused issues.
EDIT: Since "correct pointer arithmetic" was not so well understood, here is an example
struct Base1 { char mDum1; };
struct Base2 { char mDum2; };
struct Derived: Base1, Base2 {};
int main(int argc, char* argv[])
{
Derived d;
Base1* b1 = &d;
Base2* b2 = &d;
std::cout << "Base1: " << b1
<< "\nBase2: " << b2
<< "\nDerived: " << &d << std::endl;
return 0;
}
And here is what was printed:
Base1: 0x7fbfffee60
Base2: 0x7fbfffee61
Derived: 0x7fbfffee60
Not the difference between the value of b2
and &d
, even though they refer to one entity. This can be understood if one thinks of the memory layout of the object.
Derived
Base1 Base2
+-------+-------+
| mDum1 | mDum2 |
+-------+-------+
When converting from Derived*
to Base2*
, the compiler will perform the necessary adjustment (here, increment the pointer address by one byte) so that the pointer ends up effectively pointing to the Base2
part of Derived
and not to the Base1
part mistakenly interpreted as a Base2
object (which would be nasty).
This is why using C-Style casts is to be avoided when downcasting. Here, if you have a Base2
pointer you can't reinterpret it as a Derived
pointer. Instead, you will have to use the static_cast<Derived*>(b2)
which will decrement the pointer by one byte so that it correctly points to the beginning of the Derived
object.
Manipulating pointers is usually referred to as pointer arithmetic. Here the compiler will automatically perform the correct adjustment... at the condition of being aware of the type.
Unfortunately the compiler cannot perform them when converting from a void*
, it is thus up to the developer to make sure that he correctly handles this. The simple rule of thumb is the following: T* -> void* -> T*
with the same type appearing on both sides.
Therefore, you should (simply) correct your code by declaring: IClassInterface* globalMember
and you would not have any portability issue. You'll probably still have maintenance issue, but that's the problem of using C with OO-code: C is not aware of any object-oriented stuff going on.