Implement two functions with the same name but different, non-covariant return types due to multiple abstract base classes
A

3

6

If I have two abstract classes defining a pure virtual function with the same name, but different, non-covariant return types, how can I derive from these and define an implementation for both their functions?

#include <iostream>

class ITestA {
    public:
        virtual ~ITestA() {};
        virtual float test() =0;
};

class ITestB {
    public:
        virtual ~ITestB() {};
        virtual bool test() =0;
};

class C : public ITestA, public ITestB {
    public:
    /* Somehow implement ITestA::test and ITestB::test */
};


int main() {
    ITestA *a = new C();
    std::cout << a->test() << std::endl; // should print a float, like "3.14"
    ITestB *b = dynamic_cast<ITestB *>(a);
    if (b) {
        std::cout << b->test() << std::endl; // should print "1" or "0"
    }
    delete(a);
    return 0;
}

As long as I don't call C::test() directly there's nothing ambiguous, so I think that it should work somehow and I guess I just didn't find the right notation yet. Or is this impossible, if so: Why?

Amylaceous answered 7/7, 2012 at 3:9 Comment(1)
A
3

Okay, it is possible, and the way isn't too ugly. I have to add an additional level of inheritance:

 ITestA       ITestB     <-- These are the interfaces C has to fulfill, both with test()
    |           |
ITestA_X     ITestB_X    <-- These classes implement the interface by calling a
    |           |             function with a different name, like ITestA_test
    \__________/              which is in turn pure virtual again.
         |
         C               <--  C implements the new interfaces

Now C has no function test(), but when casting a C* to an ITestA*, the implementation of test() in ITestA_test will be used. When casting it to an ITestB*, even by a dynamic_cast from the ITestA*, the implementation of ITestB_test will be used. The following program prints: 3.14 0

#include <iostream>

class ITestA {
    public:
        virtual ~ITestA() {};
        virtual float test() =0;
};

class ITestB {
    public:
        virtual ~ITestB() {};
        virtual bool test() =0;
};

class ITestA_X : public ITestA {
    protected:
        virtual float ITestA_test() =0;
        virtual float test() {
            return ITestA_test();
        }
};

class ITestB_X : public ITestB {
    protected:
        virtual bool ITestB_test() =0;
        virtual bool test() {
            return ITestB_test();
        }
};

class C : public ITestA_X, public ITestB_X {
    private:
        virtual float ITestA_test() {
            return 3.14;
        }
        virtual bool ITestB_test() {
            return false;
        }
};

int main() {
    ITestA *a = new C();
    std::cout << a->test() << std::endl;
    ITestB *b = dynamic_cast<ITestB *>(a);
    if (b) {
        std::cout << b->test() << std::endl;
    }
    delete(a);
    return 0;
}

Does this have any drawbacks you could think of?

Amylaceous answered 7/7, 2012 at 7:28 Comment(0)
J
2

When you declare ITestA *a = new C(), you have created a C object. If you invoke test on it with a->test(), it has to use the C virtual table to find the code to execute. But C is trying to have two different implementations of the same signature test(), which isn't allowed. The fact that you declared it as an ITestA * doesn't affect the method resolution. You declared the methods virtual, so they are found via the actual type of the object, regardless of the type of the pointer you used to access it.

You cannot have two methods with the same name and argument types. You'll need to find another way to structure this code.

Jollenta answered 7/7, 2012 at 3:15 Comment(5)
If I implement both functions in the base classes instead of making them pure (yet keeping them virtual!), everything works out great, so the method resolution doesn't seem to be much of a problem to the compiler. class ITestA { public: virtual ~ITestA() {}; virtual float test() { return 3.14; } }; class ITestB { public: virtual ~ITestB() {}; virtual bool test() { return false; } }; class C : public ITestA, public ITestB { };Amylaceous
That's because you've provided a list of base classes, which determines which of your test method wins. Your C class only has one test method.Jollenta
C should have a c.ITestA::test and a c.ITestB::test. And when using ITestA *a = c it automatically uses c.ITestA::test. So what I need is a way to define c.ItestA::test directly instead of relying on inheritance to bring it to C (same for ITestB::test). And that's impossible? Or is the fact that it uses c.ITestA::test already non-standard and just "luck" of compiler-choice (gcc 4.4)?Amylaceous
No, C should not have two test functions. In fact, it cannot have two. Functions in C++ are distinguished by their names and argument types. Your two functions have the same name, and the same argument types. They cannot co-exist. You have to think of a different way to do what you want.Jollenta
Correct, C cannot have two functions, but every C instance, like c (small case, sorry for not picking a better name in the example), consists of an ITestA instance and ITestB instance, which both have one test function. I know as much from reading about the Diamond shaped inheritance problem. This actually brought me a solution! I'll post it as a new answer. Thanks for your help though, wouldn't have found it otherwise.Amylaceous
M
0

I don't think this is possible. Functions overload by name (and signature).

Mcclish answered 7/7, 2012 at 3:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.