Search code examples
c++multiple-inheritancevirtual-functionsoverridingabstract-base-class

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


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?


Solution

  • 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?