10

Consider the code

struct Base{};
struct Derived: public Base{};

struct A: public Base{};

struct B: public A, public Base{};

struct C: public A, public Derived{}; // why no ambiguity here?

int main() {}

The compiler (g++5.1) warns that

warning: direct base 'Base' inaccessible in 'B' due to ambiguity struct B: public A, public Base{};

I understand this, Base is duplicated in B.

  1. Why is there no warning for C? Doesn't C inherit from both A and Derived, which both inherit from Base?

  2. Why adding virtual

    struct Derived: virtual Base{};
    

results now in both B and C emitting warnings, live on Wandbox

warning: direct base 'Base' inaccessible in 'B' due to ambiguity struct B: public A, public Base{};

warning: direct base 'Base' inaccessible in 'C' due to ambiguity struct C: public A, public Derived{};

10
  • Search internet for "dreaded diamond inheritance". Commented May 15, 2015 at 19:43
  • @ThomasMatthews I know what the diamond problem is, and that's closely related to why B gives a warning. However what I don't understand is why C is OK. Commented May 15, 2015 at 19:46
  • I think it's just a case of gcc not detecting the ambiguity in C because Base is ambiguous in that case too. When you virtually derive from Base the most derived class (C) is responsible for calling the Base constructor, and so gcc starts detecting the ambiguity again. Commented May 15, 2015 at 19:57
  • @Praetorian it makes sense. clang++/vc++ don't spit any warnings at all, unless I make struct Derived: public virtual Base Commented May 15, 2015 at 20:00
  • 1
    It could also be because with the original C I can address Base's members by explicitly qualifying them. Say Base had a member function void foo(){}. Then I can say C{}.A::foo(); C{}.Derived::foo();, but as far as I can tell, there's no way to do that with B. Commented May 15, 2015 at 20:05

2 Answers 2

2

In B, it's impossible to refer to members of the Base subobject inherited directly. Consider:

struct Base {
    int x;
};

struct B: public A, public Base {
    void foo() {
        int& x1 = A::x; // OK
        int& x2 = x; // ambiguous
        // no way to refer to the x in the direct base
    }
};

In C this is not a problem. Both x's can be referred to using qualified names:

struct C: public A, public Derived {
    void foo() {
        int& x1 = A::x; // OK
        int& x2 = Derived::x; // OK
    }
};

So the warning you get is one that only makes sense when a direct base is also inherited through another path.

For your second question, I couldn't reproduce the warning with C on Coliru with g++-5.1.

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

Comments

2

There are no ways to access unambiguously to Base members in "B" whereas it's possible in "C", as illustrated in the following code:

#include <iostream>

using namespace std;

struct Base
{
    void print()
    {
        cout << "Base" << endl;
    }
};

struct Derived : public Base {};

struct A : public Base
{
    void print()
    {
        cout << "A" << endl;
    }
};

struct B : public A, public Base
{
    void print()
    {
        A::print();

        //error (ambiguous), no way to access to Base::print => warning
        //Base::print();
    }
};

struct C : public A, public Derived
{
    void print()
    {
        A::print();
        Derived::print(); // Not Ambiguous, it's the Base inherited by 'Derived' which is used.
        // Still an error but you can access print indirectly through "Derived" => no warning needed
        //Base::print();
    }
};

int main() 
{
    B b;
    b.print();

    C c;
    c.print();

    return 0; 
}

Comments

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.