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?Whenever a virtual function is called using base class reference or pointer it cannot be inlined, but whenever called using the object without reference or pointer of that class, can be inlined because the compiler knows the exact class of the object at compile time.
Virtual functions are accessible using object pointers. Redefining the virtual function in the derived class is optional, but it needs to be defined in the base class. The function call resolving is done at run-time. You can create a virtual destructor but not a constructor.
An abstract class is a class in C++ which have at least one pure virtual function. Abstract class can have normal functions and variables along with a pure virtual function. Abstract class cannot be instantiated, but pointers and references of Abstract class type can be created.
Rules for Virtual Functions Virtual functions cannot be static. A virtual function can be a friend function of another class. Virtual functions should be accessed using pointer or reference of base class type to achieve runtime polymorphism.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With