9

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;
}
larrylampco
  • 597
  • 1
  • 9
  • 33
  • 1
    Depends on how you want to fix this.. what do you want to happen here? – Karthik T Aug 10 '13 at 02:29
  • @KarthikT that's why I gave him both ways ;) – aaronman Aug 10 '13 at 02:31
  • @aaronman Yup, not faulting your solution, but not sure what he wants to acomplish with templates.. – Karthik T Aug 10 '13 at 03:07
  • @KarthikT actually I got rid of the template in the title because it seemed to have nothing to do with them – aaronman Aug 10 '13 at 03:08
  • larry as you can see in the comments templates really aren't related here so I removed them, my answer or the one at the bottom are the ways you get resolve the ambiguity – aaronman Aug 10 '13 at 03:18
  • @aaronman I had the template in the title only because that was a stab in the dark way that I thought of fixing it. I think the title is better without if that is not the best way to fix it. – larrylampco Aug 10 '13 at 03:19
  • your new idea with templates looks like better code, but in truth does a totally different thing than your original code, it's hard to know what to think without knowing your intents for the code, usually c++ has like 5 billion ways to accomplish something, all the answers currently address getting rid of the ambiguity of your method call, this update is more of a new question about restructuring your code which is unanswerable without background as to what the code does – aaronman Aug 10 '13 at 03:28
  • @aaronman, yee, I see your point. I guess making it a template means that it is either a `MeasurementKeyword` or an `CharacteristicKeyword` but not both. If I need it to be both, then it looks like specifying the namespace is the right road. – larrylampco Aug 10 '13 at 03:43
  • @larrylampco the best way is the simplest way – aaronman Aug 10 '13 at 03:44

3 Answers3

18

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.
aaronman
  • 18,343
  • 7
  • 63
  • 78
7

define AddressType like this:

class AddressType : public CharacterKeyword, public MeasurementKeyword
{
public:
    using MeasurementKeyword::GetValue;
private:
    float address;
    float addresExt;
};
WinCloud
  • 183
  • 7
6

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.

Dima
  • 38,860
  • 14
  • 75
  • 115
  • "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. – dyp Aug 10 '13 at 02:35
  • 2
    virtual does not solve this problem, the call is still ambiguous, I agree with the rest – aaronman Aug 10 '13 at 02:39
  • @DyP Yes. I mean you need to mention the parent's class. – Dima Aug 10 '13 at 02:40
  • 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! – TallChuck Jul 27 '20 at 21:51