如果我在明确的情况下使用指针,编译器可以内联一个虚函数吗?

Can a compiler inline a virtual function if I use a pointer in a clear situation?

本文关键字:一个 函数 情况下 编译器 指针 如果      更新时间:2023-10-16

我已经阅读了内联虚函数真的是没有意义的吗?但我还是有些疑惑,没有找到答案。

他们说如果情况不是模糊的,编译器应该内联虚函数

然而:

只有当编译器有一个实际的对象而不是指向对象的指针或引用时,才会发生这种情况。

那么,如果我有一个B类派生自一个A(其中包含一个virtual void doSth()函数),我使用B*指针,而不是A*:

B* b = new B;
b->doSth();
  1. 假设B没有子类。(在编译时)应该调用哪个函数是相当明显的。所以有可能是内联的。这是事实吗?
  2. 假设B有一些子类,但是这些子类没有自己的doSth()函数。所以编译器应该"知道"唯一要调用的函数是B::doSth()。我猜它不是内联的?

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也这样做。

当虚值表没有被取消引用时,可以内联虚成员函数。这可以通过显式地对成员函数进行作用域调用来实现。

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();