Ambiguous multiple inheritance of template classes
Asked Answered
R

2

11

I've got a real situation which can be summarized in the following example:

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;
}

Why is not obvious to the compiler that B is a TimeListener, and therefore the only possible overload resolution is Notifier< TimeListener >::add_listener( TimeListener& ) ?

Ruffian answered 8/3, 2016 at 17:29 Comment(1)
You may solve your issues with using Notifier<TimeListener>::add_listener; (and the other one) in struct A. DemoKermit
P
9

The lookup rules for member names say that your code is ambiguous, because the name is found in two base classes and therefore the lookup set is invalid. You don't need to be familiar with all the details of lookup sets and merging; the important detail is that both base classes are checked and the name add_listener is found in both, which creates an ambiguity.

The easy fix is to bring those base class names into A with using-declarations. This means that both versions of add_listener are looked up in A, rather than in the base classes, so there is no merge ambiguity:

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

Paste answered 8/3, 2016 at 17:43 Comment(0)
R
6

The standard indicated compiler isn't smart enough to resolve the symbol -- it's defined as an ambiguous operation despite the fact you could logically work it out in this instance. Your compiler is probably only looking for symbol names and not for prototypes after it finds both possible symbols.

You can tell the compiler that you accept both types explicitly by disambiguating the template symbols that you know should be accepted. This will get the compiler to accept either form and then apply the template. Below is an example of this. I can't test this at my computer currently but it should work if the compiler is having difficulty resolving the symbols in your original example:

struct A : public Notifier< TimeListener >
         , public Notifier< SpaceListener >
{
   using Notifier< TimeListener >::add_listener;
   using Notifier< SpaceListener >::add_listener;
};
Revels answered 8/3, 2016 at 17:41 Comment(2)
It's not that the compiler isn't smart enough, the standard says that this is ambiguous.Paste
Good point. I tried to clarify what I meant by 'not smart enough' to encapsulate the defined ambiguity.Revels

© 2022 - 2024 — McMap. All rights reserved.