I've already read Are inline virtual functions really a non-sense?. But I still have some doubts and found no answer there.
They say that if situation isn't ambiguous, compiler should inline the virtual function.
However:
This can happen only when the compiler has an actual object rather than a pointer or reference to an object.
So what if I have a B
class derived from an A
one (which contains a virtual void doSth()
function) and I use the B*
pointer, not the A*
:
B* b = new B;
b->doSth();
B
hasn't any child classes. It's rather obvious (on the compile time) what function should be called. So it's possible to be inlined. Is it in fact?B
has some child classes but these classes haven't its own doSth()
function. So compiler should "know" that the only function to call is B::doSth()
. I guess it doesn't inline though?It doesn't matter whether B
has any derived classes. In that situation b
points to a B
object so the compiler can inline the call.
And surely any decent modern compiler can and will do that in your situation. If you don't use pointers it becomes a whole lot easier. It's not really an "optimization" then. The fact that you can omit a virtual call then becomes obvious by only looking at the AST node at the left side of the .
-operator. But if you use pointers, you need to track the dynamic type of the pointee. But modern compilers are capable of that.
EDIT: Some experiment is in order.
// main1.cpp
struct A {
virtual void f();
};
struct B : A {
virtual void f();
};
void g() {
A *a = new A;
a->f();
a = new B;
a->f();
}
// clang -O2 -S -emit-llvm -o - main1.cpp | c++filt
// ...
define void @g()() {
%1 = tail call noalias i8* @operator new(unsigned int)(i32 4)
%2 = bitcast i8* %1 to %struct.A*
%3 = bitcast i8* %1 to i32 (...)***
store i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*]* @vtable for A, i32 0, i32 2) to i32 (...)**), i32 (...)*** %3, align 4
tail call void @A::f()(%struct.A* %2)
%4 = tail call noalias i8* @operator new(unsigned int)(i32 4)
%5 = bitcast i8* %4 to i32 (...)***
store i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*]* @vtable for B, i32 0, i32 2) to i32 (...)**), i32 (...)*** %5, align 4
%tmp = bitcast i8* %4 to %struct.B*
tail call void @B::f()(%struct.B* %tmp)
ret void
}
// ...
As can be seen, clang does direct calls to f
, both when a
points to a A
and when it points to a B
. GCC does that too.