Fixing C++ Multiple Inheritance Ambiguous Call

17,986

Solution 1

This is because of a diamond inheritance pattern, to resolve the error you can specify the specific namespace you want the member from like.

paddressType->MeasurementKeyword::GetValue()

or

paddressType->CharacterKeyword::GetValue()  

  1. Basically your AddressType class has access to the GetValue members from both the classes it inherits from and can't choose one (call is ambiguous).
  2. The scope resolution operator (:: ) helps specify which one you actually want.
  3. You haven't said what you actually want this code to do so I'll just say that generally complex inheritance patterns are not conducive to creating readable code, rethink what you actually want.

Solution 2

define AddressType like this:

class AddressType : public CharacterKeyword, public MeasurementKeyword
{
public:
    using MeasurementKeyword::GetValue;
private:
    float address;
    float addresExt;
};

Solution 3

Generally, when you run into the deadly diamond of death it is a sign that you should rethink your design. However, if you absolutely cannot avoid this situation, C++ provides a solution in the form of virtual inheritance. Virtual inheritance resolves some of the "diamond ambiguities", but it is also clunky. For example, you have to explicitly call the parent's non-default constructors in the derived class' constructor.

Once again, the best way is to avoid the diamond in the first place. I have been programming in C++ for many years, and so far I have never had this problem in my code.

Share:
17,986
larrylampco
Author by

larrylampco

Updated on June 28, 2022

Comments

  • larrylampco
    larrylampco almost 2 years

    I have three classes structured like this:

    #include <iostream>
    using namespace std;
    
    class Keyword
    {
        public:
            virtual float GetValue() = 0;
    };
    
    class CharacterKeyword : public Keyword
    {
        public:
            virtual float GetValue(){return _value;}
        private:
            float _value;
    };
    
    class MeasurementKeyword : public Keyword
    {
        public:
            virtual float GetValue(){return _value;}
        private:
            float _value;
    };
    
    class AddressType : public CharacterKeyword, public MeasurementKeyword
    {
    
        private:
            float address;
            float addresExt;
    };
    
    int main()
    {
        AddressType *a = new AddressType();
        a->GetValue();
        return 0;
    }
    

    I am getting the following:

    In function ‘int main()’:
    error: request for member ‘GetValue’ is ambiguous
    error: candidates are: virtual float Keyword::GetValue()
    error: virtual float MeasurementKeyword::GetValue()
    error: virtual float CharacterKeyword::GetValue()

    I have done some reading into multiple inheritance and I know that it has a lot of pitfalls - this being one of them. I need my class structure to be like this so I was wondering if there was a way that I could fix this using templates?

    Update
    After reading your comments, my original thought was that maybe I can just delineate between an AddressType that is a CharacterKeyword and an AddressType that is a MeasurementKeyword by templating the AddressType. And using it as such in the updated code. OR I can just specify the namespace of the member that I would like. Since the templated way has not been mentioned yet as an answer, is it a bad fix? Should I just specify the namespace of the member I want?

    template <class T>
    class AddressType : public T
    {
    
        private:
            float address;
            float addresExt;
    };
    
    int main()
    {
        AddressType<MeasurementKeyword> *a = new AddressType<MeasurementKeyword>();
        a->GetValue();
        return 0;
    }
    
  • dyp
    dyp almost 11 years
    "you have to explicitly call the parent's constructors in the derived class' constructor." You mean the virtual base classes are initialized in the most-derived class? If there are default ctors in the virtual base class, you don't need to call them explicitly.
  • aaronman
    aaronman almost 11 years
    virtual does not solve this problem, the call is still ambiguous, I agree with the rest
  • Pigsty
    Pigsty almost 11 years
    @DyP Yes. I mean you need to mention the parent's class.
  • TallChuck
    TallChuck almost 4 years
    I have an interesting case where A defines a pure virtual function, which B implements, but C does not, and I want D to use B's implementation. The solution was to make C a public virtual A, thanks for the answer!