Pointer-to-member-function performs virtual dispatch?
Asked Answered
M

2

10

I executed the following code.

#include <iostream>

class Base
{
public:
    virtual void func()
    {
        std::cout<<"Base func called"<<std::endl;
    }
};

class Derived: public Base
{
public:
    virtual void func()  override
    {
        std::cout<<"Derived func called"<<std::endl;
    }
};

int main()
{
    void (Base::*func_ptr)()=&Base::func; //Yes, the syntax is very beautiful.
    Base* bptr=new Derived();
    (bptr->*func_ptr)();
}

My expected output was Base func called. However, Instead, the output was

Derived func called

Which surprised me, because I think that func_ptr should be able to see only Base members(because I thought that func_ptr doesn't access the member function via _vptr, but the function address itself.

I would like to know, how the virtual dispatch takes place in this case(how the access to virtual table takes place), and where this behavior is defined in C++ standard(I couldn't find anything)?

Microbalance answered 1/9, 2021 at 20:0 Comment(5)
you might want to check out codeproject.com/Articles/7150/…Bombard
I believe this is implemented as an 'offset', so it will go off of whatever type it is pointing to from a this pointer plus the offset. Since it is pointing to an actual Derived object, that is the function that is running.Compulsive
If you think about it, when you write bptr->func, the compiler resolves func to Base::func too, only then during runtime it is resolved to the correct call.Whipperin
There is no mention of virtual tables in the standard. They are an implementation detail of specific compilers. VTables are not the only way virtual methods can be implemented, just the most common way.Ostrander
A pointer-to-member-function is not the same thing as a pointer. It has enough data to handle virtual dispatching when the member function it points at is a virtual function.Langouste
I
9

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)

Inga answered 1/9, 2021 at 20:14 Comment(4)
Is it possible to get function pointer which will not cause virtual dispatch?Avila
@MarekR take pointer for sheared object type, if you intend to cast down to base object, and reinterpret cast the pointer. It's UB and on your consciense.Accoucheur
@Swift-FridayPie if it is UB then this is not a solution. Also please provide example since your description is to vague for me.Avila
@MarekR: It's not possible to get a member function pointer, but it's certainly possible to get a function pointer if you write another function to wrap a call on a reference that will explicitly invoke the base class' member function. This can even work with a non-capturing lambda like so: godbolt.org/z/74WvGP698Inga
J
2
  1. Your understanding is incorrect.
  2. It is an implementation detail. Different compilers may implement it differently. If in doubt, look at the assembly. It is actually pretty clever in this particular compiler. If the value stored in the pointer-to-member is even, it is a straight function pointer (all functions are located at even addresses). If it is odd, it is an offset in the vtable, plus one. All vtable offsets are also even, so to get to the actual pointer, it is decremented by 1.
Jedthus answered 1/9, 2021 at 20:20 Comment(1)
"Your understanding is incorrect" - That's, Why I am here.Microbalance

© 2022 - 2024 — McMap. All rights reserved.