我可以期待这个电话被内联吗?

时间:2013-06-28 10:33:41

标签: c++ g++

A * a = new B();
a->foo();

假设B派生A而foo()是虚函数。这个例子非常频繁,问题是在某些地方,据说编译器不会尝试内联这个,在其他地方则说明完全相反。

我个人看不出这个调用无法内联的原因,因为在编译时很容易告诉哪个函数被调用。

编辑1: 我知道“一般情况”,并且我也知道编译器需要很多因素来决定是否内联。如果我问编译器可能内联这个特定的调用,问题可能会更好。

我问这个的原因是this C++ FAQ引用的特别引用:

  

当通过指针或引用引用对象时,无法内联对虚函数的调用,因为必须动态解析调用。

4 个答案:

答案 0 :(得分:4)

如果编译器能够以某种方式推断出a指向的对象是B的实例(即如果代码很简单,就像你的例子中那样),那么它就可以虚拟化呼叫 - 尽管不需要这样做。

但重点是,一般情况下,调用在运行时解决,因为您不知道(编译器也不会!)对象的动态类型是什么由a指出。

换句话说,如果编译器不知道*a的动态类型是什么,因此在运行时不知道应该调用哪个foo()函数,则不可能进行虚拟化调用并在编译时内联它。

答案 1 :(得分:2)

为了澄清一下,g ++显然至少可以正确地描述虚拟呼叫的一些微不足道的情况;这与你的情况类似,只是稍微调整以获得更可读的汇编程序:)

void test(int);

class A { public: virtual int Bop() { return 1; } };

class B : public A { virtual int Bop() { return 4711; } };

int main() {
  A* a = new B();
  test(a->Bop());
}

$ g++ -O99 -S -c test.cpp

$ less test.s
...
main:
.LFB2:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $8, %edi
    call    _Znwm
    movl    $4711, %edi            // hard coded 4711
    movq    $_ZTV1B+16, (%rax)
    call    _Z4testi               // calls test()
...

答案 2 :(得分:0)

想象一下这样的翻译单位:

<强> compute.cpp:

#include "A.h"

int compute(A * p)
{
    return p->get_some_int() * 10;
}

您如何认为虚拟函数调用可能被虚拟化?这是一个用法示例:

<强> main.cpp中:

#include "B.h"
#include "C.h"

int compute(A *);

int main()
{
    A * p = rand() % 2 == 0 ? new B : new C;
    return compute(x);
}

答案 3 :(得分:0)

你不能期望编译器内联它。这取决于编译器和选择的优化级别。鉴于B :: foo的实现是可见的,并且你在赋值和调用之间没有做任何事情,那么,是的,视觉检查表明有足够的信息可供编译器进行优化。

但它没有。

在很多其他情况下,它没有足够的信息。