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

发布于 2024-11-26 09:01:48 字数 681 浏览 1 评论 0原文

我已经读过 内联虚拟函数真的毫无意义吗? 。但我仍然有一些疑问,并没有找到答案。

他们说,如果情况不明确,编译器应该内联虚拟函数

然而:

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

那么,如果我有一个从 A 派生的 B 类(包含一个 virtual void doSth() 函数)并且我使用 < code>B* 指针,而不是 A*

B* b = new B;

b->doSth();
  1. 假设 B 没有任何子类。应该调用什么函数(在编译时)是相当明显的。所以内联是可以的。事实是这样吗?
  2. 假设 B 有一些子类,但这些类没有自己的 doSth() 函数。因此编译器应该“知道”唯一要调用的函数是B::doSth()。我猜它没有内联?

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();
  1. 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?
  2. 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

寄风 2024-12-03 09:01:48

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
}
// ...

可以看出,当 a 指向 A 和指向 B 时,clang 都会直接调用 f。海湾合作委员会也这样做。

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.

指尖上得阳光 2024-12-03 09:01:48

当 vtable 未针对调用取消引用时,可以内联虚拟成员函数。这可以通过对成员函数进行显式作用域调用来完成。

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

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文