Can't call inherited method if I overload it with a different subclass argument

43 Views Asked by At

I have these base classes to be extended and the goal was to provide some default implementation for unhandled events in the subclass.

struct Event {};

struct State {
    void handle_event(const Event& e) {
        std::cout << "State - generic event\n";
    }
};

An example of a subclass implementation:

struct FlipSwitch : public Event {};
struct OtherEvent: public Event {};

class Light : public State {
public:
    void handle_event(const FlipSwitch& e) {
        std::cout << "Light - FlipSwitch event\n";
    }
};

If I call:

Light *light = new Light();
light->handle_event(FlipSwitch());    // Works fine.
light->handle_event(OtherEvent());    // error: no matching function for call to
                                      //  ‘Light::handle_event(OtherEvent)’

If I define the default implementation on the subclass or add using State::handle_event; it fallbacks accordingly.

I expected that if the compiler didn´t find any function with 'OtherEvent' that would fallback to the generic defined in the base class just like it does when it's not inherited. I didn't expect the subclass to hide the method with a method with a different signature.

1

There are 1 best solutions below

0
Oersted On

You can achieve the desired result by fixing your inheritance scheme and using dynamic casting in order to dispatch event processing to the right handler:

#include <iostream>

// make Event polymorphic by adding at least one virtual function
struct Event {
    virtual ~Event() = default;
};

// making handle_event virtual to properly override it
struct State {
    virtual void handle_event(const Event& e) {
        std::cout << "State - generic event\n";
    }
};

struct FlipSwitch : public Event {};
struct OtherEvent : public Event {};

class Light : public State {
   public:
    // void handle_event(const FlipSwitch& e) {
    // override handle_vent
    virtual void handle_event(const Event& e) override {
        // use dynamic dispatch
        // NB using pointer so that dynamic_cast fails by returning nullptr, not
        // exception
        if (dynamic_cast<const FlipSwitch*>(&e)) {
            std::cout << "Light - FlipSwitch event\n";
        } else {
            State::handle_event(e);
        }
    }
};

int main() {
    Light* light = new Light();
    light->handle_event(FlipSwitch());  // Works fine.
    light->handle_event(
        OtherEvent());  // also works fine
    return 0;
}

Live

First make handle_event a proper virtual function so that a inherited object will call its implementation. It will use Event object of any concrete type. Then, in the overrided version, use dynamic_cast to try to cast the Eventobject to its actual class. Besides, I use dynamic_cast on pointer, not reference, si that, if the requested type is not the correct one, it will return nullptr, not throw an exception. For dynamic_cast to work, Event must be a polymorphic type, that is to say, a type that can be inherited. A way to tell that to the compiler is to declare at least one virtual function, here the destructor. If no valid type is recognised, I fall back to the default behavior, for instance, one that is implemented in the base class. Et voilà