9

У меня есть реальная ситуация, которую можно резюмировать в следующем примере:Неоднозначность множественного наследования классов шаблонов

template< typename ListenerType > 
struct Notifier 
{ 
    void add_listener(ListenerType&){} 
}; 

struct TimeListener{ }; 
struct SpaceListener{ }; 

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 

}; 

struct B : TimeListener{ }; 

int main() 
{ 
    A a; 
    B b; 

    a.add_listener(b); // why is ambiguous? 

    return 0; 
} 

Почему не очевидно, что компилятор B является TimeListener, и поэтому единственно возможным разрешение перегрузки Notifier<TimeListener>::add_listener(TimeListener&)?

+4

Вы можете решить ваши проблемы с 'использованием Notifier :: add_listener; '(и другой) в' struct A'. [Demo] (http://coliru.stacked-crooked.com/a/6e43848691a4cfcb) – Jarod42

ответ

8

Правило подстановки для имен членов говорят, что ваш код является неоднозначным, так как имя найдено в двух базовых классах, поэтому набор поиска недействителен. Вам не нужно знать все детали наборов поиска и слияния; важная деталь заключается в том, что оба базовых класса проверены, а имя add_listener найдено в обоих, что создает двусмысленность.

Легкое исправление заключается в том, чтобы довести эти имена базового класса до A с помощью объявлений-объявлений. Это означает, что обе версии add_listener ищутся в A, а не в базовых классах, так что нет слияния двусмысленность:

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 
    using Notifier<TimeListener>::add_listener; 
    using Notifier<SpaceListener>::add_listener; 
    //plus any more base classes 
}; 

Live Demo

5

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

Вы можете сообщить компилятору, что вы принимаете оба типа явно, путем неоднозначности символов шаблона, которые, как вы знаете, должны быть приняты. Это заставит компилятор принять любую форму, а затем применить шаблон. Ниже приведен пример этого. Я не могу проверить это на моем компьютере в данный момент, но он должен работать, если компилятор испытывает трудности разрешающих символов в оригинальном примере:

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 
    using Notifier<TimeListener>::add_listener; 
    using Notifier<SpaceListener>::add_listener; 
}; 
+6

Это не значит, что компилятор недостаточно умен, стандарт говорит, что это неоднозначно. – TartanLlama

+0

Хорошая точка. Я попытался прояснить, что я имел в виду под «не достаточно умным», чтобы инкапсулировать определенную двусмысленность. – Pyrce

Смежные вопросы