Почему одного виртуального наследования недостаточно для решения проблемы страшного алмаза?

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 четко упоминается ваш случай, ваше наблюдение совпадает с объясненным там.

Другие вопросы по тегам