3

I am studying C++ and while studying virtual inheritance, I came across following doubt:

class A {
public:
    int x;
    A() { x = 677; }
    A(int a) { 
        cout << "A con , x= " << a << endl;
        x = a;
    }
};
class B : public A {
public:
    B(int a) : A(a) { }
};
class C :public A {
public:
    C(int a) : A(a) { }
};
class D : public B, public C {
public:
    D(int a,int b) : B(a),C(b) { }
};
int main()
{
    D d(5,6);
    cout << d.A::x;  //prints 5 
 }

In line cout << d.A::x; It prints 5. Shouldn't this call be ambiguous and if not why it prints 5?

4
  • 1
    How many different compilers did you try? Check if GCC, Clang and MSVC all agree or not. If not, you're probably flirting with undefined behaviour. Commented Nov 15, 2021 at 13:44
  • Did you enable compiler warnings? Commented Nov 15, 2021 at 13:48
  • 1
    Latest GCC and Clang gives compile errors on Godbolt default settings. MSVC is happy to compile, but I don't read MASM well enough to be able to tell you what it would output and Godbolt doesn't have the option to execute code compiled with MSVC at the moment. Commented Nov 15, 2021 at 13:50
  • 1
    @LouisCloete different compilers not agreeing is a rather weak indicator for UB. Very often different compilers don't disagree even though there is UB. And sometimes they disagree without any UB in sight Commented Nov 15, 2021 at 13:56

2 Answers 2

2

d.A::x; is indeed ambiguous. GCC and Clang report it as error, only MSCV fails to do so: https://godbolt.org/z/1zhjdE6a8.

There is a note in [class.mi] with an example of multiple inheritance stating that:

In such lattices, explicit qualification can be used to specify which subobject is meant. The body of function C​::​f can refer to the member next of each L subobject:

void C::f() { A::next = B::next; }      // well-formed

Without the A​::​ or B​::​ qualifiers, the definition of C​::​f above would be ill-formed because of ambiguity ([class.member.lookup]). — end note]

It is just a note, because it follows from [class.member.lookup] (which is a little more contrived to read and understand) that without the qualifier the member access is ambiguous. "ill-formed" implies that a conforming compiler must issue either an error or warning.

Sign up to request clarification or add additional context in comments.

Comments

1

This is ambiguous according to both GCC and Clang, but MSVC compiles it (Godbolt)

You can solve it by using virtual inheritance for B and C if it makes sense to only have one instance of A in D or you can specify via which parent the call must access A::x if you need two distinct instances of A in D.

std::cout << d.B::x; // go through B to its A
std::cout << d.C::x; // go through C to its A

The above compiles on all 3 compilers without error, because it is unambiguous. It specifies through which intermediate class to access A::x.

2 Comments

Be careful about recommending virtual inheritance. It is not a coding solution to ambiguities; it's a design decision. Yes, it could remove the ambiguity, but if there are supposed to be two x members, changing to virtual inheritance breaks the design.
@PeteBecker I added a part about the tradeoffs with virtual inheritance and gave an alternative if you need two unique instances. Do you agree with it?

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.