user997112
user997112

Reputation: 30615

Why "inline" is incompatible with "virtual"

Can someone explain the below paragraph please, no explanation is given:

The reason it works is that, in virtually all implementations of C++, virtual functions are implemented with a "virtual function table", which is an array of pointers to functions. This is incompatible with function inlining because you simply can't have a pointer to inline code. Thus for most situations, virtual and inline are incompatible; the compiler will simply ignore the inline statement. However, there is one situation whereby they are compatible: where the object is accessed directly instead of through a pointer.

"High Performance Game Programming in C++", Paul Pedriana

Can someone please explain why the two sentences in bold are so?

Upvotes: 3

Views: 231

Answers (3)

Potatoswatter
Potatoswatter

Reputation: 137800

First, the keywords aren't incompatible in any way. The paragraph is discussing the underlying implementation. A particular function call may be either inlined or go through a virtual dispatch, but usually not both. To understand why, you just need to understand what an inline call or a virtual call is.

An inlined function call is one where at least part of the called function is spliced directly into the calling function. This is a low-level optimization that the compiler may perform even on functions that aren't defined as inline. But in the early days when compilers were dumber and executable code size was more important, the keyword was more directly related to the optimization feature.

A virtual call is one where the caller doesn't know exactly what function will be executed. The function's implementation needs to be looked up (dispatched) according to the class at runtime.

So if the function will only be determined at runtime, the compiler cannot splice two functions together. It doesn't know what function would get spliced in.

But you can call a virtual function without making a special dispatch. This occurs if the compiler can be sure of the object's actual type at compile time. For example, if dog is derived from pet with a virtual method speak,

dog fido;
fido.speak(); // direct dispatch: we know fido is a dog.

But this is not the case if we have a reference:

bird buddy;
pet &favorite = prefer_dogs? fido : buddy;
favorite.speak(); // favorite may be a bird or dog: need virtual dispatch

Most of the time, when you call a virtual function you do so through a virtual dispatch.


There is another case, which as far as I know is only theoretical: if the compiler can be sure of the whole class hierarchy (or the choices in a given instance), it could add a Boolean test whether favorite is a bird or a dog and inline both function calls as alternatives in an automatically-generated if … else statement. But anyway, this is just nuts and bolts that you shouldn't worry about.

What's important is that a virtual function is called according to the type the object was defined as, and an inline function may be defined in a header file. And that's all that matters.

Upvotes: 4

Balog Pal
Balog Pal

Reputation: 17163

It is not "incompatible", just when actually virtual call is placed it is impossible to inline the unknown code. When static call is made to the function inlining will work alright.

The text is not very well formed, but if you already know what it wants to tell, you can find it there. ;-)

Upvotes: 0

Luchian Grigore
Luchian Grigore

Reputation: 258588

It simply means the compiler won't inline a call it has to resolve at run-time.

Suppose you have

struct A
{
   virtual void foo() {};
};

and

void test(A* a)
{ 
   a->foo();
}

This call is resolved at runtime. Since foo() could have been overriden by a deriving class and a could point to an object of a derived type, a->foo() can't be inlined - it will be resolved by a lookup.

The second statement means that virtual functions could be inlined in some situations:

void test(A a)
{
   a.foo();
}

In this case, a is of type A (even if the original object passed to the function was a derived type, it was sliced because you passed by value) - the call is guaranteed to call A::foo so it can be inlined by the compiler.

Upvotes: 5

Related Questions