Наследование дважды
При двойном наследовании у вас есть неоднозначность - компилятор не может знать, какую из двух баз A вы хотите использовать. Если вы хотите иметь две базы A (иногда вы можете захотеть это сделать), вы можете выбрать между ними приведение к B или C. Наиболее подходящим из приведенных здесь значений по умолчанию является static_cast
(как самое слабое из доступных), однако он не очень нужен (он все же сильнее, чем нужно вашему случаю), так как вы не приводите к производному типу. Пользовательский шаблон safe_cast
должен выполнить работу:
/// cast using implicit conversions only
template <class To,class From>
inline To safe_cast( const From &from ) {return from;}
main()
{
D obj;
foo(safe_cast<B *>(&obj)); //error. How do i call with D's B part.
}
Типы времени компиляции - используйте шаблоны
Кроме того, в случае виртуального наследования,
почему информация о смещении должна быть
хранится в виртуальной таблице. Это может быть
определяется во время компиляции.
В приведенном выше случае, если мы передаем Foo с
Объект D, во время компиляции только мы
можно рассчитать смещение D's A
часть.
Это заблуждение. Функция foo в том виде, в каком она написана, теперь не имеет информации о типе компиляции о типе ptr, кроме A *, даже если вы передаете B * или C *. Если вы хотите, чтобы foo мог действовать в зависимости от типа прошедшего времени компиляции, вам нужно использовать шаблоны:
template <class TypeDerivedFromA>
int foo(TypeDerivedFromA *ptr)
{
ptr->eat();
}
Виртуальное наследование
В ваших вопросах упоминается виртуальное наследование. Если вы хотите использовать виртуальное наследование, вам нужно указать так:
class B: public virtual A ...
class C: public virtual A ...
При этом код будет компилироваться, но с этим решением вы не сможете выбирать между B :: A или C :: A (есть только один A), поэтому, вероятно, это не то, о чем вы.
Виртуальные функции
Более того, ваши вопросы, кажется, путают две разные концепции: виртуальное наследование (что означает разделение одного базового класса между двумя промежуточными базовыми классами) и виртуальные функции (что означает, что функция производного класса может вызываться через указатель базового класса). Если вы хотите, чтобы B :: eat вызывался с использованием указателя A, вы можете сделать это без виртуального наследования (фактически виртуальное наследование помешает вам сделать это, как описано выше), используя виртуальные функции:
class A
{
int a;
int b;
public:
virtual void eat()
{
cout<<"A::eat()"<<endl;
}
};
Если виртуальные функции для вас неприемлемы, механизмом компиляции для этого являются шаблоны, как объяснено выше.