Виртуальное наследование: ошибка: нет уникального конечного перехватчика

Я знаю, что виртуальное наследование рассматривается здесь до и перед тем, как задать этот вопрос, я просмотрел детали виртуального наследования и просмотрел детали аналогичной проблемы, например:

multiple-diamond-inheritance-compiles-without-virtual-but-doesnt-with а также почему gcc дает мне ошибку - окончательный перехват

Моя проблема несколько отличается, поскольку я не использую чистую виртуальную функцию и явно использую виртуальное наследование, чтобы иметь один уникальный класс "Base". Иерархия выглядит следующим образом:

   Base
    /\
   /  \
 Der1 Der2
   \  /
   Der3

Я знаю о страшном бриллианте по проблеме деривации и почему я использую виртуальное наследование.

#include <iostream>
class base
{
public :

    base()
    {
        std::cout<<"base()" << std::endl;
    }
    virtual void fun()
    {
        std::cout<<"base" << std::endl;
    }
};

class der1: virtual public base
{
public :
    void fun()
    {
        std::cout<<"der1" << std::endl;
    }
};

class der2 : virtual public base
{
public :
    void fun()
    {
        std::cout<<"der2" << std::endl;
    }
};

class der3 : public der1,public der2
{
    public :
    /*void fun()
    {
        std::cout<<"der3" << std::endl;
    }*/
    //if I took out the comment and the function 
    //is defined it compiles fine as expected
};

int main()
{
    base *p=new der3;
    //used scope operation explicitly to avoid ambiguity
    p->base::fun(); //here it complains about 'no unique final overrider for fun'
    return 0;
}

Мое понимание, так как я использую виртуальное наследование, должен быть только один экземпляр "базы", ​​и с помощью оператора области я могу без особых проблем вызывать функцию виртуального удовольствия. Функция не является чистой виртуальной. Если я уйду и реализую класс 'der3', это даст мне ошибку компилятора:

error: нет уникального финального переопределения для "виртуальной пустоты":: fun() в 'der3

Я вижу, как эта проблема работает (окончательный переадресация). Но у меня нет. Это путается между Base::fun, der1::fun и der2::fun? Помогает ли оператор сферы обслуживания в любом случае?

Приветствуется любая подсказка или помощь. Я использую g++ 4.6.3.

Ответ 1

Самый производный класс должен обеспечить реализацию виртуальных функций в виртуальном базовом классе - иначе как бы он обеспечивал, чтобы интерфейс базового класса, учитывая промежуточные классы (т.е. ваши der1 и der2), обеспечивал два альтернативы - на что он должен называться? Вы должны устранить ситуацию (т.е. С помощью der3::fun()).

Конечно, вы на самом деле не вызываете der3::fun(), поскольку вы явно запрашиваете base::fun(), но это не значит, что правила не применяются, не более, чем думать, что вы можете создать экземпляр абстрактного класса, t попытаться вызвать чисто виртуальные функции.... Программа плохо сформирована, пока код не свяжется с этими свободными концами.

Ответ 2

Использование оператора разрешения области видимости для указания того, что вы хотите вызвать base::fun, не приводит к ошибке, поскольку программа будет плохо сформирована даже с пустым main(). Вам просто не разрешается иметь ситуацию, в которой виртуальная функция имеет более одного конечного переопределения в любом производном классе, который существует в вашей программе.

Неформально, просто потому, что попытка вызова p->fun() была бы неоднозначной, программа плохо сформирована, даже если вы этого не сделаете.

Примечание. Это отличается от ситуации с перегруженными функциями, в которой допускается потенциальная двусмысленность - возможно, даже неизбежная - пока вы избегаете вызова, который на самом деле был бы двусмысленным. Почему разные правила? В основном это связано с тем, что даже создание объекта типа der3 не может быть сделано разумным образом --- в какой версии fun должна стоять точка vtable?