Sign in to follow this  
daviddiligent

cpp diamond problem

Recommended Posts

Another quick question. class A { virtual void eat(); }; class B: public virtual A { virtual void eat(); //override A::eat() here }; class C: public virtual A { virtual void eat(); //override A::eat() here }; class D: public B, public C {}; Now if my code is like this: D d; what's d.eat() ??? I know if B and C didn't override eat(), then d will invoke A::eat(). But, now both B and C overrides A's eat(), then will happen to D's eat() ? Thanks in advance [Edited by - daviddiligent on May 16, 2008 10:11:06 AM]

Share this post


Link to post
Share on other sites
Try it and see? I'm a bit iffy with virtual inheritance, but I believe it's a compiler error (Since eat() is ambiguous).

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Try it and see? I'm a bit iffy with virtual inheritance, but I believe it's a compiler error (Since eat() is ambiguous).


Hey Evil Steve. Nice to see you again :)
The explanation from wiki made me think about this new problem, as it only explained the situation where B and C doesn't override the virtual function.
http://en.wikipedia.org/wiki/Virtual_inheritance

Cpp is really a deep programming language ;)

Share this post


Link to post
Share on other sites
Keep in mind that the 'virtual' specifier on your methods is (almost) irrelevant here; dynamic dispatch only occurs through references or pointers, and your D instance is neither, the dispatch of the call to eat() is always statically-bound.

Your example, furthermore, will not compile. It won't compile as it stands (with the virtual methods) because there is no final overrider for eat(), thus a dynamic dispatch from an A* to eat() when the A* points (legally) at a D* cannot occur (which do you call, B::eat() or C::eat()?) This is ambiguous, so the program is ill-formed.

If you remove the virtual specifier on your methods, your program is still ill-formed because the static-bound call to eat() is ambiguous.

Quote:

Cpp is really a deep programming language ;)

That's not the word I'd use.

Share this post


Link to post
Share on other sites
as others said, it's ambigous to do that.
to resolve ambiguity, you should add an eat() method in D and call B's or C's implementation of eat() from there.
it's the programer's job to resolve ambiguity.

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
............
dynamic dispatch only occurs through references or pointers,

OK, seems to be true. But I never notice this. I thought dynamic binding is also working for the common instance ( like D d;)

Quote:
Original post by jpetrie
Your example, furthermore, will not compile. It won't compile as it stands (with the virtual methods) because there is no final overrider for eat(), thus a dynamic dispatch from an A* to eat() when the A* points (legally) at a D* cannot occur (which do you call, B::eat() or C::eat()?) This is ambiguous, so the program is ill-formed.

I know something is wrong here. But I just think that this is a part of cpp multiple inheritance problem. How exactly do programmers overcome it.

Quote:
Quote:

Cpp is really a deep programming language ;)

That's not the word I'd use.


Come on, you know I am a foreign speaker. I meant the cpp programming is really deep, which makes me think.

Thanks for the reply anyway.

Share this post


Link to post
Share on other sites
Quote:

OK, seems to be true. But I never notice this. I thought dynamic binding is also working for the common instance ( like D d;)

It can't. d is a D, and always will be a D, it can never be anything derived from a D (C++ is statically typed).

Consider another example (using your original class hierarchy): A a; a is an instance of A that is sizeof(A) in size. Now recall that any subclasses of A (B or C) must contain a subobject of type A, which means that sizeof(B) >= sizeof(A). It follows then that an instance of A can never be an instance of anything derived from A, since it's too small (in fact, attempting to assign an instance of something derived from A to an A will slice that object, leaving only the A parts behind and doing generally evil things).

For these reasons, and others, it makes no sense for dynamic dispatch to operate on non-pointer, non-reference types. It just can't work.

Quote:

I know something is wrong here. But I just think that this is a part of cpp multiple inheritance problem. How exactly do programmers overcome it.

Multiple inheritance in C++ is generally something you want to avoid, as proper, cleaner solutions can usually be found. That said, if multiple inheritance is the right solution, we avoid ambiguity issues like the one presented in your example by not creating them in the first place. This is the programmers job, to understand his language and its rules. In this case, the language says "you can't do that," (and for good reasons, as I outlined), so you simply have to work around it. It's the same way you work around not dereferencing a null pointer. Just don't do it.

Quote:

Come on, you know I am a foreign speaker.

I didn't, actually. I meant that it's corners like this that make the C++ language so horribly overcomplex and brittle. Go check out the language in section 10.2 of the standard that deals with name lookup, for example. It's horrible.


Share this post


Link to post
Share on other sites
LOL.

Man, you blow up my mind. :) thanks very much.

Btw, I guess a cleaner solution for the multiple inheritance is like to do similar things like in Java and C#.

I mean we can use interfaces rather than the real classes. But because cpp doesnt have interfaces, I think we can fake interfaces by defining pure abstract classes. Does this work?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this