4

Dire que j'ai trois classes:Comment C++ choisit-il quelle fonction surchargée appeler?

class X{}; 
class Y{}; 
class Both : public X, public Y {}; 

Je veux dire que j'ai deux classes, puis une troisième classe qui étend à la fois (héritage multiple).

dire Maintenant, j'ai une fonction définie dans une autre classe:

void doIt(X *arg) { } 
void doIt(Y *arg) { } 

et j'appelle cette fonction avec une instance à la fois:

doIt(new Both()); 

Cela provoque une erreur de compilation, indiquant que l'appel de la fonction est ambigu.

Quels sont les cas, en dehors de celui-ci, où le compilateur C++ décide que l'appel est ambigu et renvoie une erreur, le cas échéant? Comment le compilateur détermine-t-il ces cas?

+0

Il semble que, sur cette question, vous auriez pu l'essayer et l'avoir découvert. :) – dkretz

+0

oui mais je n'ai pas eu accès à un compilateur C++ – Claudiu

+0

http://codepad.org/ pour gcc et http://www.comeaucomputing.com/tryitout/ pour comeau sont deux bons sites pour des tests rapides. –

Répondre

7

simple: si elle est ambiguë, le compilateur vous donne une erreur, vous forçant à choisir. Dans votre extrait, vous obtiendrez une erreur différente, car le type de new Both() est un pointeur vers Both, alors que les deux surcharges de doIt() acceptent leurs paramètres par valeur (c'est-à-dire qu'ils n'acceptent pas les pointeurs). Si vous avez modifié doIt() pour prendre les arguments des types et Y* respectivement, le compilateur vous donnera une erreur à propos de l'appel de fonction ambigu.

Si vous voulez appeler explicitement l'un ou l'autre, vous lancez les arguments de façon appropriée:

void doIt(X *arg) { } 
void doIt(Y *arg) { } 
Both *both = new Both; 
doIt((X*)both); // calls doIt(X*) 
doIt((Y*)both); // calls doIt(Y*) 
delete both; 
+1

+1 pour une gestion correcte de la mémoire = P. – Claudiu

+0

+1 malgré l'utilisation de cast de style C (et la gestion de la mémoire manuelle) – Motti

5

Je reçois cette erreur avec gcc:

[email protected]:~/Desktop$ g++ -o test test.cpp 
test.cpp: In function ‘int main(int, char**)’: 
test.cpp:18: error: call of overloaded ‘doIt(Both&)’ is ambiguous 
test.cpp:7: note: candidates are: void doIt(X) 
test.cpp:11: note:     void doIt(Y) 
0

AFAIK le compilateur C++ toujours choisir le match le plus proche et le plus spécifique qu'il peut déterminer, au moment de la compilation.

Cependant, si votre objet est dérivé des deux, je pense que le compilateur devrait vous donner une erreur ou au moins un avertissement très sévère. L'ordre de déclaration ne devrait pas avoir d'importance puisqu'il s'agit de relations de sous-typage, et le premier objet n'est pas «plus d'un sous-type» que l'autre.

1

vous devez jeter explicitement votre argument x ou y

doIt (nouveau deux());

donc ajouter ...

(X *) ou (Y *)

comme ...

doIt ((X *) Les deux nouveaux());

+0

Vous voulez dire (X *) et (Y *), je vais corriger cela pour vous –

+1

En outre, vous devriez privilégier les lancers de style C++ –

2

Le compilateur effectue une recherche en profondeur et non une recherche étendue pour détecter les surcharges. La réponse complète est dans Herb Sutter's exceptional C++, malheureusement, je n'ai pas le livre en main.

Edit: Vous avez le livre à la main maintenant Elle est appelée la première règle de profondeur est appelée "The Interface Principle":

Le principe d'interface pour une classe X, toutes les fonctions, y compris fonctions libres, que les deux (un) "mention" X, et (b) sont "fournis par" X logiquement partie de X, parce qu'ils font partie de l'interface de X.

mais i s une règle secondaire appelée "Koenig Lookup", qui rend les choses plus difficiles.

Quote: "(simplifié): si vous fournissez un argument de fonction de type classe (ici x, de type A :: X), puis de rechercher le nom de la fonction le compilateur considère correspondant noms dans l'espace de noms (ici a) contenant le type de l'argument » -Herb Sutter, C++ exceptionnel, P120

+0

ce? – Claudiu

3

Ceci est un parfait exemple d'utilisation boost::implicit_cast:

void doIt(X *arg) { } 
void doIt(Y *arg) { } 

doIt(boost::implicit_cast<X*>(new Both)); 

Contrairement à d'autres solutions (y compris static_cast), la distribution échouera si aucune conversion implicite Both*- est possible. Cela se fait par une astuce, mieux représenté un exemple simple:

X * implicit_conversion(X *b) { return b; } 

C'est ce qui est boost::implicit_cast, juste qu'il est un modèle dont il indique le type de b.

+0

@litb pouvez-vous expliquer quelle est la différence entre static_cast et implcit_cast – yesraaj

+2

vous pouvez descendre avec static_cast. pas si avec implicit_cast. static_cast vous permet essentiellement de faire n'importe quelle conversion implicite, et en plus l'inverse de toute conversion implicite (jusqu'à certaines limites, vous ne pouvez pas downcast s'il y a une classe de base virtuelle impliquée). Mais implicit_cast n'acceptera * que * les conversions implicites. pas de down-cast, pas de vide * -> T *, pas de U-> T si T n'a que des constructeurs explicites pour U. –

Questions connexes