виртуальный метод ведет себя по-разному с множественным наследованием

почему это работает

struct Base {
    virtual void visit(const A &) { };
    virtual void visit(const B &) { };
}

и это жалуется на двусмысленность при вызове метода посещения

template< typename T >
struct X {
    virtual void visit(const T &) { };
};

struct Base: X< A >, X< B > { };

это также показывает ту же проблему:

struct Y {
   virtual void visit(const A &) { };
};

struct Z {
    virtual void visit(const B &) { };
};

struct Base: Z, Y { };

как-то это похоже на то, что множественное наследование путается с сигнатурами виртуальных функций...

с gcc версии 4.8.0 20130411 (предварительная версия)


person user2364353    schedule 09.05.2013    source источник
comment
возможный дубликат multiple-inheritance-template-class   -  person yngccc    schedule 09.05.2013
comment
Примечание: вам не нужно ; после }, заканчивающего тело функции, как в virtual void visit(const A &) { };, и это уродливо.   -  person curiousguy    schedule 14.05.2013


Ответы (1)


Здесь виртуальность не имеет значения, и тот факт, что 2 базовых класса являются шаблонными классами, не имеет значения. Более простой код с той же проблемой:

struct A {
   void f(const A &) { }
};

struct B {
    void f(const B &) { }
};

struct Der: A,B { };

Здесь Der имеет два унаследованных члена: A::f(const A &) и B::f(const B &); они могут быть вызваны следующим образом:

Der d;
d.A::f(d);
d.B::f(d);

но не таким образом:

d.f((A&)d); // error: ambiguous name lookup
d.f((B&)d); // error: ambiguous name lookup

Разрешение перегрузки функций-членов не работает так, как вы думаете (найдите оба f, а затем оставьте тот, который имеет совместимый список аргументов).

В C++ поиск по имени member находит все нескрытые объявления членов с заданным именем, поэтому здесь он находит A::f(const A &) и B::f(const B &), а затем немедленно терпит неудачу, если эти члены не объявлены в одном и том же классе.

Вы можете решить эту проблему, добавив 2 объявления в один и тот же класс:

struct Der: A,B {
    using A::f; // imports A::f(const A &)
    using B::f; // imports B::f(const B &)
};

Затем вы можете сделать:

Der d;
d.f((A&)d); // OK: Der::f(&A)

Помните: это чисто двусмысленность поиска имени члена.

person curiousguy    schedule 14.05.2013