Honza Brabec
Honza Brabec

Reputation: 37598

Can I expect this call to be inlined?

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

Suppose B derives A and foo() is a virtual function. This example is quite frequent, the problem is that in some places it is said that compiler won't attempt to inline this, where in other places the exact opposite is stated.

I personally do not see a reason why this call could not be inlined, because it is quite easy to tell in compile time which function is getting called.

Edit 1: I am aware of the "general case", and am also aware that the compiler takes many factors to decide whether to inline or not. The question would be probably better formed if I asked if the compiler might inline this particular call.

The reason I am asking this is this particular quote from this C++ FAQ which says:

When the object is referenced via a pointer or a reference, a call to a virtual function cannot be inlined, since the call must be resolved dynamically.

Upvotes: 3

Views: 146

Answers (4)

Joachim Isaksson
Joachim Isaksson

Reputation: 180917

Just to clarify, g++ apparently inlines at least some trivial cases of virtual calls correctly; this is similar to your case, just a bit tweaked to get more readable assembler :)

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

Upvotes: 2

Tom Tanner
Tom Tanner

Reputation: 9354

You can't expect the compiler to inline it. That's up to the compiler and the picked optimisation levels. Given that the implementation for B::foo is visible and that you have done nothing between the assignment and the call, then, yes, visual inspection suggests there is enough information available for the compiler to make that optimisation.

But it doesn't have to.

And in a lot of other situations, it won't have sufficient information.

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 477010

Imagine a translation unit like this:

compute.cpp:

#include "A.h"

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

How do you think that virtual function call could possibly be devirtualized? Here's a usage example:

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

Upvotes: 0

Andy Prowl
Andy Prowl

Reputation: 126432

If the compiler can somehow deduce that the object pointed by a is an instance of B (i.e. if the code is trivial, like in your example), then it could devirtualize the call - although it is not required to do so.

But the point is that, in general, the call is resolved at run-time, because you don't know (and neither does the compiler!) what is the dynamic type of the object pointed to by a.

In other words, if the compiler does not know what is the dynamic type of *a, and consequently does not know which foo() function should be called until run-time, it is impossible to devirtualize the call and inline it at compile-time.

Upvotes: 4

Related Questions