Не имеет значения, имеет ли B
какие-либо производные классы. В этой ситуации b
указывает на объект B
, поэтому компилятор может встроить вызов.
И, конечно, любой достойный современный компилятор может и сделает это в вашей ситуации. Если вы не используете указатели, это становится намного проще. Это не совсем «оптимизация» тогда. Тот факт, что вы можете опустить виртуальный вызов, становится очевидным, если взглянуть только на узел AST слева от оператора .
. Но если вы используете указатели, вам нужно отслеживать динамический тип указателя. Но современные компиляторы способны на это.
РЕДАКТИРОВАТЬ: Некоторые эксперименты в порядке.
// 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
}
// ...
Как видно, clang делает прямые звонки на f
, когда a
указывает на A
и когда он указывает на B
. GCC делает это тоже.