2009-08-21 4 views
9

Je reçois l'erreur de compilation suivante dans un de mes cours, en utilisant gcc 3.4.5 (MinGW):demande de membre `... » est ambigu en g ++

src/ModelTester/CModelTesterGui.cpp:1308: error: request for member `addListener' is ambiguous 
include/utility/ISource.h:26: error: candidates are: void utility::ISource<T>::addListener(utility::IListener<T>*) [with T = const SConsolePacket&] 
include/utility/ISource.h:26: error:     void utility::ISource<T>::addListener(utility::IListener<T>*) [with T = const SControlPacket&] 

Espérons que vous pouvez voir que ISource<T> est une interface de modèle qui indique simplement que l'objet peut être un indicateur pour un objet de type correspondant IListener<T>. Donc, la chose qui m'a contrarié, c'est cette idée que, pour une raison quelconque, les fonctions sont ambiguës quand, pour autant que je sache, elles ne le sont pas. La méthode addListener() est surchargée pour différents types d'entrée IListener<const SConsolePacket&> et IListener<const SControlPacket&>. L'utilisation est:

m_controller->addListener(m_model); 

m_model est un pointeur vers un objet IRigidBody et IRigidBody hérite seulement de IListener< const SControlPacket& > et certainement pas de IListener< const SConsolePacket& >

Comme un contrôle de santé mentale, je doxygen pour générer le diagramme de hiérarchie de classes et doxygen est d'accord avec moi que IRigidBody ne dérive pas de IListener< const SConsolePacket& >

Évidemment ma compréhension de l'héritage en C++ n'est pas exactement correcte. Je suis sous l'impression que IListener<const SControlPacket&> et IListener<const SConsolePacket&> sont deux types différents, et que les déclarations de fonction

addListener(IListener<const SConsolePacket&>* listener) 

et

addListener(IListener<const SControlPacket&>* listener) 

déclare deux fonctions distinctes qui font deux choses distinctes en fonction de la (distincte) type différent du paramètre qui est entré. En outre, je suis sous l'impression qu'un pointeur vers un IRigidBody est également un pointeur vers un IListener<const SControlPacket&> et qu'en appelant addListener(m_model) le compilateur devrait comprendre que j'appelle la deuxième des deux fonctions ci-dessus.

J'ai même essayé casting m_model comme ceci:

m_controller->addListener(
     static_cast<IListener<const SControlPacket&>*>(m_model)); 

mais toujours obtenir cette erreur. Je ne peux pas pour la vie de moi voir comment ces fonctions sont ambiguës. Quelqu'un peut-il faire la lumière sur cette question?

P.S. Je sais comment forcer la fonction à être non ambiguë en faisant ceci:

m_controller->ISource<const SControlPacket&>::addListener(m_model); 

Je viens arriver à penser que est terriblement unreadible et je préférerais ne pas avoir à le faire.

Modifier ... je plaisante. Cela ne semble résout pas le problème car il conduit à une erreur de liaison:

CModelTesterGui.cpp:1312: undefined reference to `utility::ISource<aerobat::SControlPacket const&>::addListener(utility::IListener<SControlPacket const&>*)' 
+0

Quelle est la relation entre SControlPacket et SConsolePacket? – GRB

+0

Pouvez-vous s'il vous plaît ajouter pourquoi la dernière ligne 'm_controller-> ISource :: addListener (m_model);' désambiguise l'appel? Si les fonctions sont surchargées, elles doivent être dans la même classe. Où sont ces fonctions déclarées? –

+0

@GRB Il n'y a pas de relation. Les deux sont des structures qui ne dérivent de rien. @litb J'avais tort à ce sujet. Il l'a fait compiler mais il s'avère que cela mène à une erreur de lien quand l'éditeur de liens essaye de trouver l'ISource <...> :: addListener (...) qui est pure virtuelle. Maintenant, je suis très confus. Quand vous dites déclaré je suppose que vous voulez dire défini. Ils sont définis dans les classes concerete dérivées de IController. – cheshirekow

Répondre

20

On dirait que votre situation est comme ceci:

struct A { 
    void f(); 
}; 

struct B { 
    void f(int); 
}; 

struct C : A, B { }; 

int main() { 
    C c; 
    c.B::f(1); // not ambiguous 
    c.f(1); // ambiguous 
} 

Le deuxième appel à f est ambigu, parce qu'en regardant le nom, il trouve des fonctions dans deux portées de classe de base différentes. Dans ce cas, la recherche est ambiguë - ils ne se surchargent pas mutuellement. Un correctif consisterait à utiliser une déclaration using pour chaque nom de membre.Lookup trouver des noms dans le cadre de C et ne pas rechercher de plus:

struct C : A, B { using A::f; using B::f; }; 

Maintenant, l'appel trouverait deux fonctions, faire la résolution de surcharge, et constater que celui qui prend int s'adaptera. Reporté à votre code, cela signifierait que vous devez faire quelque chose comme ce qui suit

struct controller : ISource<const SConsolePacket&>, ISource<const SControlPacket&> { 
    using ISource<const SConsolePacket&>::addListener; 
    using ISource<const SControlPacket&>::addListener; 
}; 

Maintenant, les deux noms sont dans la même portée, et maintenant ils peuvent surcharger l'autre. Lookup va maintenant s'arrêter à la classe de contrôleur, ne pas plonger plus loin dans les deux branches de la classe de base.

+0

NICE! Alors c'est à quoi servent ces choses. Je ne pense pas avoir vraiment compris l'utilisation des déclarations (une conséquence de ne pas vraiment comprendre la recherche de noms). Merci beaucoup. En fait, je place la déclaration using dans l'interface IController. Je pense que c'est l'endroit approprié pour cela. – cheshirekow

+0

Au moins en g ++, deux utilisations ne sont pas autorisées. Vous choisissez soit A :: f ou B :: f dans la classe dérivée C. – Dingle

+0

Merci! Comme toujours une réponse très claire et concise: D –

Questions connexes