去虚拟化非最终方法

Devirtualizing a non-final method

本文关键字:方法 虚拟化      更新时间:2023-10-16

假设我有一个像下面这样的类设置:

class A {
  public:
    virtual void foo() { printf("default implementationn"); }
};
class B : public A {
  public:
    void foo() override { printf("B implementationn"); }
};
class  C : public B {
  public:
    inline void foo() final { A::foo(); }
};
int main(int argc, char **argv) {
  auto c = new C();
  c->foo();
}

一般来说,对c->foo()的调用可以解虚拟化并内联到printf("default implementation")调用吗?例如在gcc中,这是有保证的吗?我的直觉是A::foo()是非虚拟的,因为类是显式指定的,因此printf将始终是内联的。

你问的是优化,所以通常我们必须选择一个编译器并尝试它。我们可以查看程序集的输出,以确定编译器是否按照您想要的方式进行了优化。

让我们试试GCC 5.2:

.LC0:
    .string "B implementation"
B::foo():
    movl    $.LC0, %edi
    jmp puts
.LC2:
    .string "default implementation"
A::foo():
    movl    $.LC2, %edi
    jmp puts
C::foo():
    movl    $.LC2, %edi
    jmp puts
main:
    subq    $8, %rsp
    movl    $8, %edi
    call    operator new(unsigned long)
    movl    $.LC2, %edi
    call    puts
    xorl    %eax, %eax
    addq    $8, %rsp
    ret

让我们试试Clang 3.6:

main:                                   # @main
    pushq   %rax
    movl    $.Lstr, %edi
    callq   puts
    xorl    %eax, %eax
    popq    %rdx
    retq
.Lstr:
    .asciz  "default implementation"

在这两种情况下,你都可以很清楚地看到所有的虚函数都被内联了。

"这是保证的吗,例如在gcc中?"

如果编译器对对象的实际类型有信心,那么我怀疑这种优化总是会发生。但是我没有任何证据来支持这个说法。