9

Ho una situazione reale che si può riassumere nel seguente esempio:ambiguo ereditarietà multipla di classi template

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

Perché non è evidente per il compilatore che B è un TimeListener, e quindi l'unica possibile la risoluzione di sovraccarico è Notifier<TimeListener>::add_listener(TimeListener&)?

+4

Si può risolvere i vostri problemi con 'utilizzando Notifier :: add_listener; '(e l'altro) in' struct A'. [Demo] (http://coliru.stacked-crooked.com/a/6e43848691a4cfcb) – Jarod42

risposta

8

Le regole di ricerca per i nomi dei membri dicono che il codice è ambigua, perché la il nome si trova in due classi base e quindi il set di ricerca non è valido. Non è necessario avere familiarità con tutti i dettagli dei set di ricerca e della fusione; il dettaglio importante è che entrambe le classi di base sono spuntate e il nome add_listener si trova in entrambi, il che crea un'ambiguità.

La soluzione facile consiste nel portare tali nomi di classi base in A con le dichiarazioni di utilizzo. Ciò significa che entrambe le versioni di add_listener sono guardato in A, piuttosto che nelle classi base, quindi non c'è alcuna ambiguità unione:

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

Il compilatore standard indicato non è abbastanza intelligente per risolvere il simbolo: è definito come un'operazione ambigua nonostante sia possibile eseguirlo logicamente in questa istanza. Probabilmente il tuo compilatore cerca solo i nomi dei simboli e non i prototipi dopo aver trovato entrambi i simboli possibili.

Si può dire al compilatore che si accettano entrambi i tipi esplicitamente disambigliando i simboli del modello che si sa dovrebbe essere accettato. Ciò consentirà al compilatore di accettare entrambi i moduli e quindi applicare il modello. Di seguito è riportato un esempio di questo. Non riesco a provare questo al mio computer attualmente ma dovrebbe funzionare se il compilatore sta avendo difficoltà a risolvere i simboli nella sua originale esempio:

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

Non è che il compilatore non sia abbastanza intelligente, lo standard dice che questo è ambiguo. – TartanLlama

+0

Buon punto. Ho cercato di chiarire cosa intendevo per "non abbastanza intelligente" per incapsulare l'ambiguità definita. – Pyrce

Problemi correlati