Funzione con lo stesso nome ma firma diversa nella classe derivata


91

Ho una funzione con lo stesso nome, ma con una firma diversa in una base e classi derivate. Quando cerco di utilizzare la funzione della classe base in un'altra classe che eredita da quella derivata, ricevo un errore. Vedere il codice seguente:

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

Ricevo il seguente errore dal compilatore gcc:

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

Se rimuovo int foo(int i){};dalla classe B, o se lo rinomino da foo1, tutto funziona bene.

Qual è il problema con questo?


1
Tecnicamente un duplicato di questa domanda, ma questa ha un titolo e risposte migliori.
Troubadour

Risposte:


77

Le funzioni nelle classi derivate che non sovrascrivono le funzioni nelle classi base ma che hanno lo stesso nome lo faranno nascondono altre funzioni con lo stesso nome nella classe base.

È generalmente considerata una cattiva pratica avere funzioni nelle classi derivate che hanno lo stesso nome di funzioni nella classe basso che non sono destinate a sovrascrivere le funzioni della classe base poiché ciò che stai vedendo di solito non è un comportamento desiderabile. Di solito è preferibile dare a funzioni diverse nomi diversi.

Se è necessario chiamare la funzione di base, sarà necessario definire l'ambito della chiamata utilizzando A::foo(s). Si noti che questo disabiliterebbe anche qualsiasi meccanismo di funzione virtuale per A::foo(string)allo stesso tempo.


13
leggi anche la risposta di litdb: puoi 'scoprire' la funzione di base con una clausola 'using A :: foo' in B.
xtofl

È vero, stavo solo cercando una soluzione che potesse essere utilizzata nel sito della chiamata, trattando la gerarchia di base come fissa.
CB Bailey

2
Qual è la base di questa affermazione e seguito dal consiglio: "È generalmente considerata una cattiva pratica avere funzioni nelle classi derivate che hanno lo stesso nome di funzioni nella classe basso che non sono destinate a sovrascrivere le funzioni della classe base come quello che vedete di solito non è un comportamento desiderabile. Di solito è preferibile dare a funzioni differenti nomi differenti " . E se facessero semanticamente la stessa cosa? Tuttavia, C ++ fornisce una soluzione al problema causato da questo, come spiegato dalla risposta di Johannes.
Nawaz

107

È perché la ricerca del nome si interrompe se trova un nome in una delle tue basi. Non guarderà oltre in altre basi. La funzione in B ombreggia la funzione in A. Devi dichiarare nuovamente la funzione di A nell'ambito di B, in modo che entrambe le funzioni siano visibili dall'interno di B e 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);
    }
};

Modifica: la descrizione reale fornita dallo Standard è (da 10.2 / 2):

I passaggi seguenti definiscono il risultato della ricerca del nome in un ambito di classe, C. Innanzitutto, viene considerata ogni dichiarazione per il nome nella classe e in ciascuno dei suoi sottooggetti della classe base. Un nome membro f in un oggetto secondario B nasconde un nome membro f in un oggetto secondario A se A è un oggetto secondario della classe base di B. Qualsiasi dichiarazione così nascosta viene eliminata dalla considerazione. Ciascuna di queste dichiarazioni che è stata introdotta da una dichiarazione d'uso è considerata provenire da ogni sottooggetto di C che è del tipo contenente la dichiarazione designata dalla dichiarazione d'uso.96) Se l'insieme di dichiarazioni risultante non è tutto da sotto-oggetti dello stesso tipo, o l'insieme ha un membro non statico e include membri da sotto-oggetti distinti, c'è un'ambiguità e il programma è mal formato. Altrimenti quel set è il risultato della ricerca.

Ha quanto segue da dire in un altro posto (appena sopra):

Per un'espressione id [ qualcosa come "foo" ], la ricerca del nome inizia nell'ambito della classe di this; per un id-qualificato [ qualcosa come "A :: foo", A è uno specificatore-nome-annidato ], la ricerca del nome inizia nell'ambito dell'identificatore-nome-annidato. La ricerca del nome avviene prima del controllo dell'accesso (3.4, clausola 11).

([...] messo da me). Nota che significa che anche se il tuo foo in B è privato, il foo in A non verrà comunque trovato (perché il controllo dell'accesso avviene in seguito).


litb, grazie per la tua risposta. Ma quando provo a compilare il tuo codice, ottengo: impossibile regolare l'accesso alla void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in classe B 'a causa del metodo locale `int B :: foo (int)' con lo stesso nome. Forse è perché uso una vecchia versione di gcc
Igor Oks

1
sì, sicuramente un bug del compilatore. i vecchi compilatori usavano "A :: foo;" invece di "usare A :: foo;" ma il primo è deprecato in C ++.
Johannes Schaub - litb
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.