-2

If I have a base abstract class with a function that takes it's own class as a parameter:

class Component abstract
{
public:
    virtual bool Method(Component& other) = 0;
};

And I have a child class which overrides and overloads this function to take a parameter of its class:

class DerivedComponent : public Component
{
public:
    virtual bool Method(Component& other) override;
    virtual bool Method(DerivedComponent& other);
};

And I use these as components of another class, as such:

class Foo
{
private:
    Component* m_component;
public:
    Foo()
    {
        m_component = new DerivedComponent();
    }

    Component* GetComponent()
    {
        return m_component;
    }
};

And I call the component's method, passing in Foo's component:

Foo foo1 = Foo();
Foo foo2 = Foo();
foo1.GetComponent()->Method(*foo2.GetComponent());

Why does it call the DerivedComponent's first un-overloaded method, Method(Component& other), instead of Method(DerivedComponent& other)?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 3
    Because `*foo2.GetComponent()` is a `Component`, so that matches the first overload. Overload resolution is based on static types. – Raymond Chen Apr 11 '23 at 20:13
  • Does this answer your question? [Calling overloaded method with derived class](https://stackoverflow.com/questions/25698355/calling-overloaded-method-with-derived-class) – Raymond Chen Apr 11 '23 at 20:16
  • 1
    What is your actual goal here? This looks like an [XY problem](https://xyproblem.info/). – Paul Sanders Apr 11 '23 at 20:40

2 Answers2

3

The compiler does not know you are using a derived type, and will not automatically up-cast a pointer to that type. GetComponent returns a Component*. That can be any subclass, not just a DerivedComponent*.

If you know the Component* is actually a DerivedComponent*, you can explicitly cast it yourself:

auto derivedComponent1 = static_cast<DerivedComponent&>(*foo1.GetComponent());
auto derivedComponent2 = static_cast<DerivedComponent&>(*foo2.GetComponent());
derivedComponent1.Method(derivedComponent2);

If you don't know, and RTTI is enabled, then you can dynamic_cast:

auto derivedComponent1 = dynamic_cast<DerivedComponent*>(foo1.GetComponent());
auto derivedComponent2 = dynamic_cast<DerivedComponent*>(foo2.GetComponent());
if (derivedComponent1 && derivedComponent2)
    derivedComponent1->Method(*derivedComponent2);

As you can see from the mess above, this is not particularly ideal. Typically that points to a more fundamental issue with your design that you may want to reconsider.

At the very least, if you need special behavior for a DerivedComponent, you could move the dynamic_cast stuff into your implementation instead of expecting the caller to do it:

class DerivedComponent : public Component
{
public:
    bool Method(Component& other) override
    {
        auto derivedComponent = dynamic_cast<DerivedComponent*>(&other);
        if (derivedComponent) {
            return Method(*derivedComponent);
        }

        // default behavior, perhaps invoking superclass method but
        // in this case that is pure virtual so I guess do nothing.
        return false;
    }

    virtual bool Method(DerivedComponent& other)
    {
        return true;
    }
};

And the above will now behave how you expected with the following:

foo1.GetComponent()->Method(*foo2.GetComponent());
paddy
  • 60,864
  • 6
  • 61
  • 103
2

DerivedComponent declares two overloads of Method: one that that takes a Component & and one that takes a DerivedComponent &.

But overloading is always resolved statically. That is, the compiler has to decide at compile time which overloaded function is going to be called. Since that resolution happens a compile time, it's based on information that's available at compile time: the static type of pointer/reference the itself, not the dynamic type of the object to which it points/refers.

There is a fairly clean way to deal with it though--the visitor pattern (or at least a close variant of it).

To implement it we, add another virtual function (which I've called Dispatch). When we use Method, it calls Dispatch on the object that was passed (by reference). So, we can get behavior that depends on both the object that did the calling and the the object that was passed. Here's a somewhat expanded version of your demo, showing virtual behavior for both the invoking object, and the passed object:

#include <iostream>

class Component
{
public:
    virtual void Method(Component& other) { 
        std::cout << this->Dispatch() << "(" << other.Dispatch() << ")\n";
    }

    virtual std::string Dispatch() { return "Component"; };
};

class DerivedComponent : public Component
{
public:
    std::string Dispatch() override { return "Derived"; }
};

struct Derived2 : public Component {
    std::string Dispatch() override { return "Derived2"; }
};

// making this a template so it's easy to create `Foo`s that create and 
// manage any of the preceding types.
template <class T>
class Foo
{
private:
    Component* m_component;
public:
    Foo()
    {
        m_component = new T;
    }

    Component* GetComponent()
    {
        return m_component;
    }
};

int main() { 
    Foo<Component> foo;
    Foo<DerivedComponent> foo1;
    Foo<Derived2> foo2;

    foo.GetComponent()->Method(*foo1.GetComponent());
    foo1.GetComponent()->Method(*foo2.GetComponent());
    foo2.GetComponent()->Method(*foo.GetComponent());
}

Result:

Component(Derived)
Derived(Derived2)
Derived2(Component)

Of course, in real use we need to handle a few other things--Foo probably needs a dtor to destroy the Component (or derived) object it creates. It probably also needs to do something with respect to copying, moving and assignment (but exactly what it should do is rather a separate question). Since we're using it as a base class, and probably going to destroy derived objects via a pointer/reference to the base, Component needs a virtual dtor. (And there may easily be a few more things that don't occur to me at the moment--but we're using inheritance, so we need to do all the usual inheritance "stuff").

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111