C++ inherit from multiple base classes with the same virtual function name

前端 未结 4 1922
自闭症患者
自闭症患者 2020-11-28 16:47

I tried this code:

class A
{
    virtual void foo() = 0;
};

class B
{
    virtual void foo() = 0;
};

class C : public A, public B
{
    //virtual void A::f         


        
相关标签:
4条回答
  • 2020-11-28 16:56

    I stepped into the same problem and accidentially opened a second thread. Sorry for that. One way that worked for me was to solve it without multiple inheritance.

    #include <stdio.h>
    
    class A
    {
    public:
        virtual void foo(void) = 0;
    };
    
    class B
    {
    public:
        virtual void foo(void) = 0;
    };
    
    
    class C
    {
        class IA: public A
        {
            virtual void foo(void)
            {
                printf("IA::foo()\r\n");
            }
        };
        class IB: public B
        {
            virtual void foo(void)
            {
                printf("IB::foo()\r\n");
            }
        };
    
        IA m_A;
        IB m_B;
    public:
        A* GetA(void)
        {
            return(&m_A);
        }
    
        B* GetB(void)
        {
            return(&m_B);
        }
    };
    

    The trick is to define classes derived from the interfaces (A and B) as local classes (IA and IB) instead of using multiple inheritance. Furthermore this approach also opens the option to have multiple realizations of each interface if desired which would not be possible using multiple inheritance. The local classes IA and IB can be easily given access to class C, so the implementations of both interfaces IA and IB can share data.

    Access of each interface can be done as follows:

    main()
    {
        C test;
        test.GetA()->foo();
        test.GetB()->foo();
    }
    

    ... and there is no ambiguity regarding the foo method any more.

    0 讨论(0)
  • 2020-11-28 16:57

    You can resolve this ambiguity with different function parameters.

    In real-world code, such virtual functions do something, so they usually already have either:

    1. different parameters in A and B, or
    2. different return values in A and B that you can turn into [out] parameters for the sake of solving this inheritance problem; otherwise
    3. you need to add some tag parameters, which the optimizer will throw away.

    (In my own code I usually find myself in case (1), sometimes in (2), never so far in (3).)

    Your example is case (3) and would look like this:

    class A
    {
    public:
        struct tag_a { };
        virtual void foo(tag_a) = 0;
    };
    
    class B
    {
    public:
        struct tag_b { };
        virtual void foo(tag_b) = 0;
    };
    
    class C : public A, public B
    {
        void foo(tag_a) override;
        void foo(tag_b) override;
    };
    
    0 讨论(0)
  • 2020-11-28 17:01

    You've got just one virtual function foo:

    class A {
        virtual void foo() = 0;
    };
    
    class B {
        virtual void foo() = 0;
    };
    
    class C : public A, public B {
        virtual void foo();
    
    };
    
    void C::foo(){}
    void C::A::foo(){}
    void C::B::foo(){};
    
    int main() {
        C c;
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-28 17:12

    A function overrides a virtual function of a base class based on the name and parameter types (see below). Therefore, your class C has two virtual functions foo, one inherited from each A and B. But a function void C::foo() overrides both:

    [class.virtual]/2

    If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list, cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf.

    As I already stated in the comments, [dcl.meaning]/1 forbids the use of a qualified-id in the declaration of a (member) function:

    When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers [...]"

    Therefore any virtual void X::foo(); is illegal as a declaration inside C.

    The code

    class C : public A, public B
    {
        virtual void foo();
    };
    

    is the only way AFAIK to override foo, and it will override both A::foo and B::foo. There is no way to have two different overrides for A::foo and B::foo with different behaviour other than by introducing another layer of inheritance:

    #include <iostream>
    
    struct A
    {
        virtual void foo() = 0;
    };
    
    struct B
    {
        virtual void foo() = 0;
    };
    
    struct CA : A
    {
        virtual void foo() { std::cout << "A" << std::endl; }
    };
    
    struct CB : B
    {
        virtual void foo() { std::cout << "B" << std::endl; }
    };
    
    struct C : CA, CB {};
    
    int main() {
        C c;
        //c.foo();  // ambiguous
    
        A& a = c;
        a.foo();
    
        B& b = c;
        b.foo();
    }
    
    0 讨论(0)
提交回复
热议问题