Многоуровневый базовый класс множественного наследования

Рассмотрим код

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() {}

Компилятор (g++ 5.1) предупреждает, что

предупреждение: прямая база 'Base' недоступна в 'B' из-за двусмысленности struct B: public A, public Base{};

Я понимаю это, Base дублируется в B.

  • Почему нет предупреждения для C? Не наследует C как от A, так и от Derived, которые оба наследуют от Base?

  • Зачем добавлять virtual

    struct Derived: virtual Base{};
    

теперь выводится как в сообщениях B, так и C, нажимая на Wandbox

предупреждение: прямая база 'Base' недоступна в 'B' из-за двусмысленности struct B: public A, public Base{};

предупреждение: прямая база 'Base' недоступна в 'C' из-за двусмысленности struct C: public A, public Derived{};

Ответ 1

В B невозможно ссылаться на членов субобъекта Base, унаследованных напрямую. Рассмотрим:

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
    }
};

В C это не проблема. Оба x могут ссылаться на использование квалифицированных имен:

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

Таким образом, вы получите предупреждение, которое имеет смысл только тогда, когда прямая база также наследуется по другому пути.

Для вашего второго вопроса я не смог воспроизвести предупреждение с помощью C на Coliru с g++ - 5.1.

Ответ 2

Нет никаких способов однозначного доступа к элементам Base в "B", тогда как это возможно в "C", как показано в следующем коде:

#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 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; 
}