Неоднозначное множественное наследование шаблонных классов

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

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& )?

2 ответа

Решение

Правила поиска для имен членов говорят, что ваш код неоднозначен, потому что имя найдено в двух базовых классах и, следовательно, набор поиска недопустим. Вам не нужно знать все детали наборов поиска и слияния; важная деталь в том, что проверяются оба базовых класса и имя 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

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

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

struct A : public Notifier< TimeListener >
         , public Notifier< SpaceListener >
{
   using Notifier< TimeListener >::add_listener;
   using Notifier< SpaceListener >::add_listener;
};
Другие вопросы по тегам