Почему одного виртуального наследования недостаточно для решения проблемы страшного алмаза?
struct B { int i; };
struct D1 : virtual B {};
struct D2 : B {}; // <-- not virtual
struct DD : D1, D2 {};
Закодировав выше, еще требует компилятор D2
также быть virtual
:
DD d;
d.i = 0; // error: request for member `i' is ambiguous
Что я не понимаю, так это то, что как только вы предложили компилятору B
является virtual
в отношении DD
(с помощью D1
) то почему это еще i
неоднозначно?
(Если моя память работает правильно, старый VC++ (в 2006 году) был достаточно способен разобрать это только с одним virtual
наследование)
3 ответа
B не виртуален по отношению к DD - он виртуален по отношению к D1. В момент создания D2 он содержит полную копию B. Таким образом, теперь DD имеет две реализации B: одну как часть D2, а другую в конце (указанную D1). И имея две копии i
Использование его действительно неоднозначно.
Если бы D2 также использовал виртуальное наследование, вместо того, чтобы содержать копию B, он содержал бы указатель на экземпляр B, на который также указывает D1, а DD содержал бы только один экземпляр B.
Я постараюсь проиллюстрировать макеты памяти, надеюсь, что это правильно получается:
Ваш случай с одним виртуальным наследством и одним не виртуальным -
| D1 | D2 + B | B |
+--+-------+----------+---------+
| vptr to B ^
+-----------------------|
Наличие как D1, так и D2 фактически наследует -
| D1 | D2 | B |
+--+-----+---+----+-------+
| | ^
+---------+---------|
Стандарт требует, чтобы d.i
должно быть двусмысленным в этом случае. Раздел 10.1.6 ИСО / МЭК 14882:2003 охватывает классы с виртуальными и не виртуальными базовыми классами данного типа.
Вы должны прочитать Алмазную проблему. Под заголовком Подходы для CPP четко упоминается ваш случай, ваше наблюдение совпадает с объясненным там.