4

Consider this small program:

#include <iostream>

struct Base {
    virtual void say_hello() {
        std::cout << "Hello from Base" << std::endl;
    }
};

struct Derived : public Base {
    void say_hello() override {
        std::cout << "Hello from Derived" << std::endl;
    }
};

int main(void) {
    Derived d;

    d.Base::say_hello();          // prints "Hello from Base"
    (d.*(&Base::say_hello))();    // prints "Hello from Derived"

    return 0;
}

When compiled and executed, it prints:

Hello from Base
Hello from Derived

As far as I understand, objects of both the Base and Derived classes are polymorphic objects and somehow contain runtime type information. That way the right overridden function can be choosen even if for base class pointers pointing to a derived object. I also learned that the overridden function from the baseclass is still available and can be called with an explicit call like:

object.Base::function()

In the small program above we can see, that this indeed works as expected. The say_hello method from the base class is selected in the d.Base::say_hello() expression and prints "Hello from Base".

What puzzles me now:

  • Why does the second case not call the base class method?
  • Is there a way to call the base member function through a member function pointer?
  • And is there even a difference between the following two expressions?
    &Base::function
    &Derived::function
    
Jakob Stark
  • 3,346
  • 6
  • 22
  • 2
    [\[conv.mem\]/2](https://timsong-cpp.github.io/cppwp/n4861/conv.mem#2). – dfrib Jan 31 '22 at 14:38
  • The reference of @dfrib gives the answer to bullet 1 and 3. It is not (easily) found in the duplicate references. – nielsen Jan 31 '22 at 14:51
  • 1
    Regarding point 3, one important difference is that `&Derived::function` can't be used with a `Base*`. – François Andrieux Jan 31 '22 at 14:53
  • One solution is to add a second function which is not `virtual` which stores the implementation and have the `virtual` forward the call to the non-`virtual` one. Alternatively you can store a `std::function` with a lambda that does the call to the specific override you want. – François Andrieux Jan 31 '22 at 14:55
  • @dfrib thanks for the reference. I had to read it a couple of times and still have the feeling that I understood only half of the paragraph. I think the core statement is this one: *The result is the same as if indirecting through the pointer to member of B with the B subobject of D*. But doesn't that mean, that the call should be done like with a B object (which would call the base method)? – Jakob Stark Jan 31 '22 at 15:14
  • Unfortunately, the question was closed before I could post my answer explaining the reference of @dfrib. Note this sentence: *The result refers to the member in D's instance of B* and *The result is the same as if indirecting through the pointer to member of B with the B subobject of D*. Hence, to call the base function, the object pointer must be casted to base type as in `((Base)d.*(&Base::say_hello))();`. On the other hand, `((Base)d.*(&Derived::say_hello))();` will fail since the pointer `&Derived::say_hello` does not belong to `Base`. – nielsen Jan 31 '22 at 18:17

0 Answers0