6

У меня проблема с определенной задачей, это упражнение, а не настоящая программа. Задача состоит в том, чтобы определить конструктор копирования структуры D, который ведет себя точно так же, как и конструктор копирования, сгенерированный компилятором.Неоднозначный вызов конструктора копирования в C++, вызванный множественным наследованием

class Ob{ 
}; 

struct A { 
Ob a; 
}; 

struct B : A { 
Ob b; 
}; 

struct C : A, B { 
Ob c; 
}; 

struct D : C, A { 
Ob d; 
}; 

Как вы можете видеть, структура А косвенно выводится несколько раз в структуре D, что приводит к неоднозначности в определении конструктора копии, как это:

D(const D& _d) : C(_d), A(_d), d(_d.d) {} 

Мой вопрос состоит в том, как определить эту копию конструктор правильно? Код без определения, упомянутого выше, компилируется, поэтому кажется, что это должно быть возможно.

MinGW сообщение 4.8.1 ошибки:

zad3.cpp:12:8: warning: direct base 'A' inaccessible in 'C' due to ambiguity  [enabled by default] 
struct C : A, B { 
     ^
zad3.cpp:16:8: warning: direct base 'A' inaccessible in 'D' due to ambiguity  [enabled by default] 
struct D : C, A { 
    ^
zad3.cpp: In copy constructor 'D::D(const D&)': 
zad3.cpp:17:38: error: 'A' is an ambiguous base of 'D' 
D(const D& _d) : C(_d), A(_d), d(_d.d) {} 
            ^

ВАЖНОГО ЗАМЕЧАНИЕ: Это НЕ DUPLICATE из вопроса: "НЕДОСТУПНОЙ прямой базы от множественного наследования", который был об общих базовых классах с разными спецификаторами доступа и был в конечном итоге вызван проблемой преобразования. Здесь речь идет об устранении неоднозначности требуемого инициализатора для общей базы, унаследованной несколько раз с той же видимостью.

+0

Можете ли вы добавить полное сообщение об ошибке компилятора? – Angew

+0

Ohh Я пропустил понимание, yee компилятор ошибка plz. –

+0

Возможно, это полезно: http://stackoverflow.com/questions/6488772/refer-base-class-members-from-derived-class – Jack

ответ

0

Конструктор копирования, сгенерированный компилятором, вызовет конструктор по умолчанию базового класса grand-father. Это, вероятно, не то, что вы хотите. Чтобы иметь чистый дизайн с вызываемыми только конструкторами копирования, вы должны реализовать конструктор копирования каждого класса и убедиться, что они вызываются по мере необходимости. В текущей иерархии, например, вы не можете бросить непосредственно из D А.

A(const A& other), a(other.a) { 
} 
//... 
B(const B& other) : A(other), b(other.b) { 
} 
//... 
C(const C& other) : B(other), A((const B)other), c(other.c) { 
} 
//... 
D(const D& other) : C(other), A((const B)other), d(other.d) { 
} 

Дальнейшее редактирование:

Но для начала, и, чтобы избежать много проблем неоднозначности в вашем exercice, вам должен был использовать virtual inheritance.

В вашем случае существует двусмысленный путь от C до A: либо C-> A, либо C-> B-> A Чтобы объявить A как общий предок в иерархии, вы объявляете наследование из B или C в A как виртуальные. Существует также двусмысленный путь от D до A: либо D-> A, либо (D-> C-> A, или D-> C-> B-> A). Таким образом, вы объявляете также наследование от D до A как виртуальное.

struct B : public virtual A { ... } 
struct C : public virtual A, public B { ... } 
struct D : public C, public virtual A { ... } 

В настоящее время существует только один общий экземпляр в иерархии. Затем вы можете написать конструктор D копию, как вы хотите:

D(const D& other) : C(other), A(other), d(other.d) 

Член D :: A :: а будет такой же, как D :: C :: A :: В или D :: С :: B :: A :: а.

+0

Да, он компилируется. ** Но ** мы имеем здесь ** множественное наследование ** без виртуальных баз. В этом конструкторе копирования вы определяете, как скопировать базу A, из которой B наследует. Он не копирует базу A из того, что D наследует напрямую, или базу A, из которой наследуется C! Эти двое получают активацию со своим конструктором по умолчанию !!! – Christophe

+0

Это не эквивалентно конструктору копирования по умолчанию. – sth

+0

Да, мой первый ответ был неточным. Я закончил. – chrphb

5

ВНИМАНИЕ: ОТВЕТ ФУНДАМЕНТАЛЬНО ИЗОБРАЖЕН!

Анализ проблемы:

Вы используете multiple inheritance with a diamond problem.

В частности, ваша структура D наследует одно и то же основание A класс три раза: один раз прямо (struct D: C,A) и дважды косвенно (через наследование C). Поскольку базовые классы не являются виртуальными, есть 3 различных суб-объекты для D. C++ 11 Стандарт разделе 10.1/4-5 называет это решетки:

inheritance diagram

Как правило, вы должны были бы рассортировать членов каждого A с явной квалификацией, сообщающей компилятору, какой из подобъектов 3 A вы ссылаетесь. Это объясняется в C++ 11, раздел 10.1/5. Синтаксис для членов должен быть A::a, C::a и B::a в пределах D, каждый из которых в конце концов предшествует D::, если вы находитесь на улице.

К сожалению, имя элемента поиск логика в C++ 11 раздела 10.2/5-6 гарантирует, что прямой A база ВСЕГДА делает другие косвенные A основ неоднозначны, несмотря на явную квалификацию (или даже using заявления).

Окончательное решение:

Поскольку проблема вызвана непосредственным базовым классом, а также тот факт, что не существует способов diambiguate этот от других, только действительно рабочий раствор использовать пустой посредник класс, чтобы заставить другое имя:

struct Ob{ int v; }; // v aded here to allow verification of copy of all members 
struct A { Ob a; }; 
struct B : A { Ob b; }; 
struct A1 : A {};  // intermediary class just for diambiguation of A in C 
struct C : A1, B { Ob c; }; // use A1 instead of A 
struct A2 : A { };  // intermediary class just for diambiguation of A in D 
struct D : C, A2 {  // use A2 instead of A 
    Ob d; 
    D() { } 
    D(const D& _d) : C(_d), A2(_d), d(_d.d) { } 
}; 

int main(int ac, char**av) 
{ 
    cout << "Multiple inheritance\n"; 
    D x; 
    x.A2::a.v = 1; // without A2:: it's ambiguous 
    x.A1::a.v = 2; // without A1:: it's ambiguous 
    x.B::a.v = 3; 
    x.b.v = 4; 
    x.d.v = 5; 

    D y = x; 
    cout << "The moment of truth: if not 1 2 3 4 5, there's a problem!\n"; 
    cout << y.A2::a.v << endl; 
    cout << y.A1::a.v << endl; 
    cout << y.B::a.v << endl; 
    cout << y.b.v << endl; 
    cout << y.d.v << endl; 
} 

Этот код компилируется и работать с MSVC2013, лязгом 3.4.1 и GCC 4.9.


Другие (не) решения:

Мой предыдущий ответ был основан только на явной квалификации. Несмотря на многие критические замечания, я действительно скомпилировал и протестировал на MSVC2013! Однако их было странно: в редакторе intelisence подчеркивалась двусмысленность, но компиляция прошла отлично без ошибок. Сначала я подумал, что это ошибка в интеллекте, но теперь понимаете, что это был несобственный компилятор (ошибка?)

Ответ, предлагающий D(const D& other) : C(other), A((const B)other), d(other.d), компилирует, но не проходит тест. Зачем ? потому что A((const B)other) поймет other как B. Таким образом, A непосредственно в D получит инициализацию со значением A, косвенно унаследованным от B (так еще A). Это очень неприятная ошибка, и мне потребовалось некоторое время, чтобы заметить.

Конечно, вы можете использовать виртуальные базовые классы. Тогда в D будет только один подобъект A, который решает многие проблемы.Однако я не конструирую то, что вы разрабатываете, и для некоторых конструкций требуется решетка, а не виртуализованный алмаз.

Если вы можете позволить себе копию с двумя шагами (шаг 1: инициализация базы по умолчанию, шаг 2: копирование целевого значения на базе), есть, конечно, подходы, использующие двухуровневые функции-члены, возвращающие ссылку на правильную базу. Но это может быть более сложным и подверженным ошибкам, чем простое решение, представленное выше.

+1

Я уже пробовал, что мне это казалось разумным, но, к сожалению, он не компилируется ни на MinGW 4.8.1, ни на Microsoft Visual C++ 2013.Оба компилятора генерируют похожие сообщения об ошибках: «A» является двусмысленной базой «D», и этот класс «A» уже инициализирован – borrisso

+1

Предлагаемое решение не компилируется. Неоднозначно, что субобъект 'A'' _d' должен использоваться для инициализации базового класса. – sth

+0

Я скомпилировал весь код выше в MSVC 2013. Но внимание: intellisense выделил ошибку для двусмысленности, о которой не сообщается при генерации кода. И скомпилированный код работал отлично (как и должно быть, потому что это действительная конструкция). – Christophe

Смежные вопросы