Fixing C++ Multiple Inheritance Ambiguous Call

2019-05-01 11:55发布

问题:

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;
}

回答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.


回答2:

define AddressType like this:

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


回答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.