Virtual Dispatch Implementation Details

Virtual dispatch implementation details

1. Do I have any errors in the above description?

All good. :-)

2. How does the compiler know f's position in vtable

Each vendor will have their own way of doing this, but I always think of the vtable as map of the member function signature to memory offset. So the compiler just maintains this list.

3. Does this mean that if a class has two bases then it has two vptrs? What is happening in this case?

Typically, compilers compose a new vtable which consists of all the vtables of the virtual bases appended together in the order they were specified, along with the vtable pointer of the virtual base. They follow this with the vtable functions of the deriving class. This is extremely vendor-specific, but for class D : B1, B2, you typically see D._vptr[0] == B1._vptr.

multiple inheritance

That image is actually for composing the member fields of an object, but vtables can be composed by the compiler in the exact same way (as far as I understand it).

4. What's happening in a diamond hierarchy with A on top B,C in the middle and D at the bottom? (A is a virtual base class of B and C)

The short answer? Absolute hell. Did you virtually inherit both the bases? Just one of them? Neither of them? Ultimately, the same techniques of composing a vtable for the class are used, but how this is done varies way to wildly, since how it should be done is not at all set in stone. There is a decent explanation of solving the diamond-hierarchy problem here, but, like most of this, it is quite vendor-specific.

Should virtual dispatch happen when a virtual method is called within a virtual method using object?

Entirely up to the implementation. Nominally it's a virtual call, but you're not entitled to assume that the emitted code will actually perform an indirection through a vtable or similar.

If foo() is called on some arbitrary B*, then of course the code emitted for foo() needs to make a virtual call to bar(), since the referand might belong to a derived class.

This isn't an arbitrary B*, this is an object of dynamic type B. The result of a virtual or non-virtual call is exactly the same, so the compiler can do what it likes ("as-if" rule), and a conforming program can't tell the difference.

Specifically in this case, if the call to foo is inlined, then I'd have thought that the optimizer has every chance of de-virtualizing the call to bar inside it, since it knows exactly what's in the vtable (or equivalent) of obj. If the call isn't inlined, then it's going to use the "vanilla" code of foo(), which of course will need to do some kind of indirection since it's the same code used when the call is made on an arbitrary B*.

Is virtual dispatch used if class type is known?

Since both the stack and the vtable are implementation details, it's probably better to phrase it:

Can the compiler use static - rather than virtual - dispatch if the object's (real, runtime) type is statically known?

to which the answer is: yes. Anywhere the compiler knows for certain what version of a virtual method will be used, it can just emit a regular statically-dispatched function call.

Note that there are some places you might expect the compiler to know the object's runtime type and be mistaken - specifically inside constructors.

If you want to know whether a particular compiler does emit this particular optimization for some particular code (at a particular optimization level), just check the assembly output. Even if you're not sure what both versions of a call should look like, you can compare the output with a simple and a fully-qualified call (b.B::foo() vs b.foo()). I'd expect gcc and clang to do a reasonable job in this case, but it's easy enough to check.

How to reliably force virtual dispatch on an object's method?

You access the inactive member of a union. The behaviour of the program is undefined.

all members of the union are subclasses of State, which means no matter what member of the union is active, I can still use the field state

It does not mean that.

A solution is to store a pointer to the base object separately. Furthermore, you'll need to keep track of which union state is currently active. This is simplest to solve using variant class:

class U {
public:
U() {
set<IdleState>();
}

// copy and move functions left as an exercise
U(const U&) = delete;
U& operator=(const U&) = delete;

State& get() { return *active_state; }

template<class T>
void set() {
storage = T{};
active_state = &std::get<T>(storage);
}
private:
State* active_state;
std::variant<IdleState, State> storage;
};

// usage
U mem;
std::cout << mem.get().do_the_thing();

Pointer-to-member-function performs virtual dispatch?

Refer to [expr.call], specifically here

[If the selected function is virtual], its final overrider in the dynamic type of the object expression is called; such a call is referred to as a virtual function call

Whether you call the function through a pointer or by class member access is the same (ref); the actual function called for a virtual function ultimately depends on the actual type of the object it is being called on.

A few (non-normative) notes in the standard under [class.virtual] say much the same thing:

[Note 3: The interpretation of the call of a virtual function depends on the type of the object for which it is called (the dynamic type)

[Note 4: [...] a virtual function call relies on a specific object for determining which function to invoke.


If you would like to know how the virtual function dispatch takes place, you will need to seek out a specific implementation, because the how is not standardized.

(I really enjoyed the article A basic glance at the virtual table, which shows one possible way you could implement it using C)



Related Topics



Leave a reply



Submit