21

If you have a struct like this one

struct A {
    void func();
};

and a reference like this one

A& a;

you can get a pointer to its func method like this:

someMethod(&A::func);

Now what if that method is virtual and you don't know what it is at run-time? Why can't you get a pointer like this?

someMethod(&a.func);

Is it possible to get a pointer to that method?

Chris
  • 6,642
  • 7
  • 42
  • 55

3 Answers3

36

Pointers to members take into account the virtuality of the functions they point at. For example:

#include <iostream>
struct Base
{
    virtual void f() { std::cout << "Base::f()" << std::endl; }
};

struct Derived:Base
{
    virtual void f() { std::cout << "Derived::f()" << std::endl; }
};


void SomeMethod(Base& object, void (Base::*ptr)())
{
    (object.*ptr)();    
}


int main()
{
    Base b;
    Derived d;
    Base* p = &b;
    SomeMethod(*p, &Base::f); //calls Base::f()
    p = &d;
    SomeMethod(*p, &Base::f); //calls Derived::f()    
}

Outputs:

Base::f()
Derived::f()
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 1
    Thanks! I didn't realize a member function pointer would take that into account. – Chris Jul 19 '11 at 22:25
  • 2
    @Chris: Member function pointers are smart. That's why they can be larger than usual function pointers. They even take into account the offset of a base-class subobject in case of multiple inheritance – Armen Tsirunyan Jul 19 '11 at 22:30
12

The way to invoke a function pointer is to also provide its object's instance pointer. This will take care of all virtuality issues:

struct A { void func(); };

int main()
{
  typedef void (A::*mf)();

  A x; // irrelevant if A is derived or if func is virtual

  mf f = &A::func;   // pointer-to-member-function
  A* p = &x;         // pointer-to-instance

  (p->*f)();           // invoke via pointer
  (x.*f)();            // invoke directly
}

OK, interesting syntax challenge question: Suppose I have this.

struct Basil { virtual void foo(); virtual ~Basil(); };
struct Derrek : public Basil { virtual void foo(); };

Now if I have Derrek * p or a Basil * p, I can invoke the Basil member via p->Basil::foo(). How could I do the same if I was given a void(Derrek::*q)() = &Derrek::foo?

Answer: It cannot be done. The PMF q alone does not know whether it points to a virtual function, let alone which one, and it cannot be used to look up a base class function at runtime. [Thanks to Steve and Luc!]

Sergey
  • 1,552
  • 20
  • 18
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I think you mean p->*f(). (but now say that func is virtual and could either be A::func or B::func - you don't know what it is. How can you get a pointer to the right one?) – Chris Jul 19 '11 at 22:23
  • Chris, I edited the typo. In you original question, you do not need to worry, you will always call the correct polymorphic function. – Kerrek SB Jul 19 '11 at 22:27
  • @Chris, at the low level, the member function pointer, for virtual functions, contains a vtable index, not an actual function pointer. When the function pointer is invoked the compiler will look up the index in the vtable and call the function in question. – bdonlan Jul 19 '11 at 22:28
  • Exactly. Calling via PTMF invokes the exactly same machinery that a straight call to `x.func()` would cause if `x` is polymorphic, namely a lookup in the class's vtable of the most derived location of `func`. – Kerrek SB Jul 19 '11 at 22:30
  • The braces around `(p->*f)` are not optional. Without them the precedence rules will stop it from compiling. – Martin York Jul 19 '11 at 22:43
  • @Martin: Thanks, I had forgotten about the parentheses :-) – Kerrek SB Jul 19 '11 at 22:43
  • 4
    "syntax challenge" so given a pointer to an unknown derived class member function, you want to call the base class member function of the same name? I don't think you can, although I might be wrong. The point about `p->Basil::foo()` is that you're using a fully-qualified name to refer to the method of `Basil` - given just a pointer you don't know the name of the function and hence you can't use that same symbol in a fully-qualified name. – Steve Jessop Jul 19 '11 at 22:49
  • @Steve: Just discussing that with Luc, apparently it can't be done at all, you're right! The pointer alone does not contain enough information. I'll edit this! – Kerrek SB Jul 19 '11 at 22:55
1

You can get a pointer to it, like so:

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

typedef void (A::*LPFOO)();

LPFOO pFoo = &A::foo;
bdonlan
  • 224,562
  • 31
  • 268
  • 324