9

J'ai une situation réelle qui peut se résumer dans l'exemple suivant:Ambigu héritage multiple de classes de modèle

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

Pourquoi n'est pas évident au compilateur que B est un TimeListener, et donc le seul possible la résolution de surcharge est Notifier<TimeListener>::add_listener(TimeListener&)?

+4

Vous pouvez résoudre vos problèmes avec 'l'aide notificateur :: add_listener; '(et l'autre) dans' struct A'. [Démo] (http://coliru.stacked-crooked.com/a/6e43848691a4cfcb) – Jarod42

Répondre

8

Les règles de consultation pour les noms des membres disent que votre code est ambigu, parce que le Le nom est trouvé dans deux classes de base et l'ensemble de recherche n'est donc pas valide. Vous n'avez pas besoin de connaître tous les détails des ensembles de recherche et de fusionner; le détail important est que les deux classes de base sont vérifiées et que le nom add_listener se trouve dans les deux, ce qui crée une ambiguïté.

La solution facile est d'amener ces noms de classe de base dans A avec les déclarations d'utilisation. Cela signifie que les deux versions de add_listener sont recherchés dans A, plutôt que dans les classes de base, donc il n'y a pas d'ambiguïté de fusion:

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

Le compilateur indiqué standard n'est pas assez intelligent pour résoudre le symbole - il est défini comme une opération ambiguë malgré le fait que vous pouvez le résoudre logiquement dans cette instance. Votre compilateur est probablement à la recherche de noms de symboles et non de prototypes après avoir trouvé les deux symboles possibles.

Vous pouvez dire au compilateur que vous acceptez explicitement les deux types en désambiguant les symboles de modèle dont vous savez qu'ils doivent être acceptés. Cela permettra au compilateur d'accepter l'un ou l'autre formulaire, puis d'appliquer le modèle. Voici un exemple de ceci. Je ne peux pas tester cela à mon ordinateur actuellement, mais il devrait fonctionner si le compilateur est d'avoir des difficultés à résoudre les symboles dans votre exemple original:

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

Ce n'est pas que le compilateur n'est pas assez intelligent, la norme dit que c'est ambigu. – TartanLlama

+0

Bon point. J'ai essayé de clarifier ce que je voulais dire par «pas assez intelligent» pour encapsuler l'ambiguïté définie. – Pyrce