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

J'ai une fonction avec le même nom, mais avec une signature différente dans une base et les 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 B, ou si je le renomme de foo1, Tout fonctionne bien.

Quel est le problème avec ça?

78
demandé sur YSC 2009-01-04 18:02:59

2 réponses

Fonctions dans les classes dérivées qui ne supplantent pas les fonctions dans les classes de base, mais qui ont le même nom masquer 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 dans la classe bass qui ne sont pas destinées à remplacer les fonctions de la 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érents fonctions différents noms.

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

66
répondu CB Bailey 2009-01-04 15:22:45

C'est parce que la recherche de nom s'arrête si elle trouve un nom dans l'une de vos bases. Il ne regardera pas au-delà dans d'autres bases. La fonction dans B ombres la fonction dans A. Vous devez déclarer à nouveau 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 description réelle donnée par 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. nom dans la classe et dans chacun de ses sous-objets de classe de base est considéré. Un nom de membre f dans un sous- l'objet B cache un nom de membre f dans un sous-objet a Si A est un sous-objet de classe de base de B. toutes déclarations qui sont si cachés sont éliminés de la considération. Chacune de ces déclarations qui a été introduite par un using-declaration est considéré comme provenant de chaque sous-objet de C du type contenant la declara- désigné par la déclaration d'utilisation.96) Si l'ensemble résultant de les déclarations ne proviennent pas toutes de sous-objets de même type, ou l'ensemble a un membre non statique et inclut des membres provenant 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 dans un autre endroit (juste au-dessus):

Pour un id-expression [quelque chose comme "foo"], recherche de nom commence dans le domaine de la classe de cela, qualifiés-id [quelque chose comme "A::foo", A est un spécificateur-nom-imbriqué ], la recherche de nom commence dans la portée du spécificateur-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 foo dans B est privé, le foo dans A ne sera toujours pas trouvé (car le contrôle d'accès se produit plus tard).

93
répondu Johannes Schaub - litb 2009-01-04 15:24:04