1

I am having some troubles with member function pointers. How do I fix this, and why doesn't this work? The issue is inside main()... or so I am guessing!

#include <iostream>
#include <functional>

template<typename T>
using FnctPtr = void(T::*)(); // pointer to member function returning void

class Base {    
public:
    Base() : vtable_ptr{ &virtual_table[0] } {}
    void foo() {
        std::cout << "Base";
    }
    void x() { 
        std::cout << "X";
    }

 
private:
    // array of pointer to member function returning void
    inline static FnctPtr<Base> virtual_table[2] = { &Base::foo, &Base::x };

public:
    FnctPtr<Base>* vtable_ptr;
};

class Derived : public Base {
public:
    Derived() {
        vtable_ptr = reinterpret_cast<FnctPtr<Base>*>(&virtual_table[0]);
    }
    void foo() /* override */ {
        std::cout << "Derived";
    }

public:
    inline static FnctPtr<Derived> virtual_table[2] = { &Derived::foo, &Base::x };
};

int main() {
    Base* base = new Base();
    base->vtable_ptr[0](); // Issue here
    delete base;
}
9
  • 1
    What does "doesn't work" mean? Why is Derived in the example? You are not using it. Commented May 12, 2022 at 15:47
  • @user17732522 yeah I realized that. But nonetheless, there's this error that I can't fix: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘* base->Base::vtable_ptr (...)’, e.g. ‘(... ->* * base->Base::...... Commented May 12, 2022 at 15:48
  • The error message is telling you what the correct syntax is... Commented May 12, 2022 at 15:54
  • Yeah, I don't know why I didn't make it work. I guess I got confused a bit. By the way, I believe the reinterpret_cast here should be fine. I'm not at all sure and I'll look again just to make sure... Commented May 12, 2022 at 16:00
  • 1
    No, they are not similar in the sense of the strict aliasing rule, see en.cppreference.com/w/cpp/language/…. But that isn't the point anyway. When using reinterpret_cast on a member pointer it behaves similar to reinterpret_cast on function pointers. The only thing the result is good for is casting back to the original type. See 10) in the link above. static_cast is required because the upcast may require adjustment of the pointer's value. Similar to how pointers to class objects cannot be downcast with reinterpret_cast safely. Commented May 12, 2022 at 16:42

1 Answer 1

2

The type of vtable_ptr is void (Base::**)() while the type of base->vtable_ptr[0] is void (Base::*)(). The syntax that you're using for the call is incorrect.

How do I fix this

The correct syntax of using the pointer to member function void (Base::*)() in your example would be as shown below:

(base->*(base->vtable_ptr[0]))();

Note that there was no need for the Derived class in the minimal reproducible example that you've provided.

Working demo

Sign up to request clarification or add additional context in comments.

4 Comments

Woah this is SO tricky! It works like a charm, thanks a lot!
Yes, once you understand that we're using pointer to members and so we must use ->*(in case of pointer) or .* (in case of object) then it is easy to fine IMO. You're welcome.
The inner parentheses are optional and vtable_ptr[0] could be replaced with *vtable_ptr (both equally fine) and then the syntax is exactly what the compiler's error message is suggesting as well.
@user17732522 Yes, they are optional. I like parenthesizing the expression wherever i can to be more explicit. So that the next time i read that these kind of long expressions, i don't have to think much about what is going on again. Its a personal preference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.