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();
- Suppose that the
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?
- Suppose that the
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.
A virtual member function can be inlined when the vtable is not dereferenced for the call. This can be accomplished by making an explicitly scoped call to the member function.
class A
{
protected:
int a;
public:
inline virtual void Func()
{
a = 0;
}
};
class B : public A
{
public:
inline virtual void Func()
{
a = 1;
}
};
B *obj = new B();
obj->Func(); // Calls B::Func() through vtable;
obj->A::Func(); // Inlines calls to A::Func();
obj->B::Func(); // Inlines calls to B::Func();