11

I came across the diamond problem and found different solutions for different cases with a single diamond. However I couldn't find a solution for 'chained' diamonds.

According to the structure: yes, I want to have multiple baseclasses everytime, so virtual inheritance isn't a solution (is it even called diamond then?). I also wanted to avoid get/set-functions for every middle-layer of a diamond.

p   p
|   |
k   k
 \ /
  s

class parent { int val; };
class kid1 : public parent {};
class kid2 : public parent {};
class school : public kid1, public kid2 {};

Accessing val in the parent class works now like follows:

school* s = new school;
s->kid1::val=1; // works

But what about the next 'chained' diamond:

p   p   p   p
|   |   |   |
k   k   k   k
 \ /     \ /
  s       s
  |       |
  c       c
    \   /
      w

class country1 : public school {};
class country2 : public school {};
class world : public country1, public country2 {};

Accessing val via:

world* w = new world;
w->country1::kid1::val=1; // error

results in:

error: ‘kid1’ is an ambiguous base of ‘world’

Why? Isn't the route to the value well defined?

5
  • 1
    @Nikolay: I think @nem knows that. But for each country we have an unambiguous base kid1. Hence the question, why this doesn't work Commented Mar 18, 2011 at 14:26
  • 2
    I'm pretty sure the school : kid relationship violates the LSP. At least I have never been to a school that was two kids :) Commented Mar 18, 2011 at 14:40
  • I fear for the sanity of whoever ends up maintaining this code base - it even drove poor Visual Studio insane. The IDE registers this as an error, but the compiler doesn't. Commented Mar 18, 2011 at 14:46
  • this was just hypothetical :) i have a different implementation. Commented Mar 18, 2011 at 14:53
  • 1
    @Fred, @Jon: agreed, it would be much better to use composition here than inheritance Commented Mar 18, 2011 at 14:53

2 Answers 2

3

s->kid1::val does not mean "val from the kid1 subobject". It's just a name qualified by the type (not the subobject) that contains it.

I don't know why country1::kid1 is even accepted at all, but apparently it's a typedef for ::kid1. Two data members in world both have the qualified name ::kid1::val.

What you want is:

world* w = new world;
country1* const c1 = world;
c1->kid1::val = 1;
Sign up to request clarification or add additional context in comments.

Comments

1

It is. The error is due to a bug in your compiler.

12 Comments

Considering I've already explained why the code is wrong, any claim the code is right should come with a citation of the particular part of the C++ standard that you think allows it :)
§3.4.3 Qualified name lookup is quite clear. Given w->country1::kid1::val, the compiler uses §3.4.5 Class member access to look up country1; according to §3.4.5/4, if the id-expression is a qualified-id (it is), this lookup takes place in both the class scope, and the context of the complete expression, so the compiler finds ::country1. Following that, the usual rules in §3.4.3 apply.
@James: And then the rule is applied again (there are two scope resolution operators), and the compiler finds ::kid1 (as I said in my answer). But ::kid1::val is ambiguous, the compiler is correct. There is no country1::kid1 apart from ::kid1, scope resolution operators work on type names, not subobject names (and base subobjects aren't named, so you can't say w->country1.kid1.val).
@Ben Voigt Which rule? After the compiler has found country1, it's in a qualified name lookup (§3.4.3), and doesn't (or shouldn't) look anywhere but in country1.
@James: What is w->country1::kid1? A type? Then w->country1::kid1::val is ambiguous, because that type is twice a base class of world. A subobject? Then it's illegal to follow it with ::.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.