Fonction avec le même nom mais une signature différente dans la classe dérivée

91

J'ai une fonction avec le même nom, mais avec une signature différente dans une base et des classes dérivées. Lorsque j'essaie d'utiliser la fonction de la classe de base dans une autre classe qui hérite du dérivé, je reçois une erreur. Voir le code suivant:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Je reçois l'erreur suivante du compilateur gcc:

In member function `void C::bar()': no matching function for call to `C::foo(std::string&)' candidates are: int B::foo(int)

Si je supprime int foo(int i){};de la classe Bou si je la renomme foo1, tout fonctionne bien.

Quel est le problème avec ça?

Igor Oks
la source
1
Techniquement, une copie de cette question mais celle-ci a un meilleur titre et des réponses.
Troubadour le

Réponses:

77

Les fonctions des classes dérivées qui ne remplacent pas les fonctions des classes de base mais qui ont le même nom masqueront les autres fonctions du même nom dans la classe de base.

Il est généralement considéré comme une mauvaise pratique d'avoir des fonctions dans des classes dérivées qui ont le même nom que des fonctions de la classe de basse qui ne sont pas destinées à remplacer les fonctions de classe de base car ce que vous voyez n'est généralement pas un comportement souhaitable. Il est généralement préférable de donner à différentes fonctions des noms différents.

Si vous devez appeler la fonction de base, vous devrez étendre l'appel à l'aide de A::foo(s). Notez que cela désactiverait également tout mécanisme de fonction virtuelle pour A::foo(string)en même temps.

CB Bailey
la source
13
lisez aussi la réponse de litdb: vous pouvez 'afficher' la fonction de base par une clause 'using A :: foo' dans B.
xtofl
Certes, je cherchais juste une solution qui pourrait être utilisée sur le site d'appel, en traitant la hiérarchie de base comme fixe.
CB Bailey
2
Quelle est la base de cette affirmation et suivie du conseil: "Il est généralement considéré comme une mauvaise pratique d'avoir des fonctions dans des classes dérivées qui ont le même nom que des fonctions de la classe de basse qui ne sont pas destinées à remplacer les fonctions de classe de base comme ce que vous voyez n'est généralement pas un comportement souhaitable. Il est généralement préférable de donner des noms différents à différentes fonctions » . Et s'ils faisaient sémantiquement la même chose? C ++ vous fournit une solution au problème causé par cela, comme expliqué par la réponse de Johannes.
Nawaz
107

C'est parce que la recherche de nom s'arrête si elle trouve un nom dans l'une de vos bases. Il ne cherchera pas au-delà dans d'autres bases. La fonction de B masque la fonction de A. Vous devez re-déclarer la fonction de A dans la portée de B, de sorte que les deux fonctions soient visibles depuis B et C:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
    using A::foo;
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Edit: La vraie description que donne la norme est (à partir de 10.2 / 2):

Les étapes suivantes définissent le résultat de la recherche de nom dans une portée de classe, C. Tout d'abord, chaque déclaration du nom dans la classe et dans chacun de ses sous-objets de classe de base est prise en compte. Un nom de membre f dans un sous-objet B masque un nom de membre f dans un sous-objet A si A est un sous-objet de classe de base de B. Toutes les déclarations ainsi masquées sont éliminées de la considération. Chacune de ces déclarations qui a été introduite par une déclaration d'utilisation est considérée comme étant de chaque sous-objet de C qui est du type contenant la déclaration désignée par la déclaration d'utilisation.96) Si l'ensemble de déclarations résultant n'est pas tous à partir de sous-objets du même type, ou l'ensemble a un membre non statique et inclut des membres de sous-objets distincts, il y a une ambiguïté et le programme est mal formé. Sinon, cet ensemble est le résultat de la recherche.

Il a ce qui suit à dire à un autre endroit (juste au-dessus):

Pour une id-expression [ quelque chose comme "foo" ], la recherche de nom commence dans la portée de classe de this; pour un identifiant qualifié [ quelque chose comme "A :: foo", A est un spécificateur de nom imbriqué ], la recherche de nom commence dans la portée du spécificateur de nom imbriqué. La recherche de nom a lieu avant le contrôle d'accès (3.4, clause 11).

([...] mis par moi). Notez que cela signifie que même si votre toto dans B est privé, le toto dans A ne sera toujours pas trouvé (car le contrôle d'accès se produit plus tard).

Johannes Schaub - litb
la source
litb, merci pour votre réponse. Mais quand j'essaye de compiler votre code, j'obtiens: impossible d'ajuster l'accès à la void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in classe B 'à cause de la méthode locale `int B :: foo (int)' avec le même nom. C'est peut-être parce que j'utilise une ancienne version de gcc
Igor Oks
1
ouais, certainement un bogue du compilateur. les anciens compilateurs utilisaient "A :: foo;" au lieu de "using A :: foo;" mais le premier est obsolète en C ++.
Johannes Schaub - litb le