Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this function call really ambiguous?

I'm learning about multiple inheritance and the diamond problem, and when I make a function call from the most derived class Visual Studio tells me that the call is ambiguous:

struct A
{
    virtual void aFunction() { cout << "I am A\n"; }
};
struct B : A {};
struct C : A {};
struct D : B, C {};

int main()
{
   D DObj;
   DObj.aFunction();             //  This is an ambiguous call
}

I understand if I had overridden the base class function in the B and C classes then the call would be ambiguous, but isn't "aFunction()" the same in B and C?

Also, making B and C inherit from A virtually makes the error go away. But my understanding of the keyword "virtual" when inheriting, ie., (Derived : virtual Base), is that it prevents a "more derived class" further down the chain from inheriting multiple copies of a Base up the chain. In inheritance multiple copies of member variables can be inherited, but only one copy of a function with the same name. So for example I could have 5 Derived classes each deriving from Base, and then a MostDerivedClass inheriting from all the 5 Derived classes, in the MostDerivedClass I would have 5 copies of the Base class "member variables", but only one copy of a function with the same name.

So in other words the "virtual" keyword for inheritance should prevent multiple Base "member variable" copies. I don't understand why it would clear up an ambiguous function call in this case.

EDIT: Thank you, it's slowly sinking in. It was impossible for me to imagine "two copies" of A in D, because A is empty (no size). But then I remembered that C++ never creates empty classes, on my setup for example an empty class has size 1. Then I was able to imagine "two copies" of A in D, and it's starting to make sense now.

like image 668
Zebrafish Avatar asked Mar 10 '23 06:03

Zebrafish


2 Answers

The call is ambiguous because there are two possible A base objects that could be passed as the this argument of the call. Even though it is the same physical function that ulitimately get called, and that function completely ignores its this argument, the fact that there are two of them makes it ambiguous.

Using virtual inheritance means that there would be only one A base object, so then the call would not be ambiguous.

like image 95
Chris Dodd Avatar answered Mar 20 '23 18:03

Chris Dodd


Because multiple copies of member variables are inherited, you could have two separate copies of a function with different behavior.

struct A
{
    int x;
    virtual void aFunction() { cout << "I am A with value " << x ; }
};
struct B : A {
};
struct C : A {
};
struct D : B, C {};

int main()
{
   D DObj;
   ((B*)(&DObj))->x = 0; // set the x in B to 0
   ((C*)(&DObj))->x = 1; // set the x in C to 1
   DObj.aFunction();             //  This is an ambiguous call
}

Should this output 0 or 1?

The compiler could detect the specific case of an inline function that does not reference this, but you can easily work around the issue so its not worth the complexity for a relatively rare case.

like image 39
user1937198 Avatar answered Mar 20 '23 19:03

user1937198