Regole di scoping quando si eredita - C ++
Domanda
Stavo leggendo le FAQ C ++ 0x di Stroustrup e mi sono bloccato con questo codice. Considera il seguente codice
struct A
{
void f(double)
{
std::cout << "in double" << std::endl;
}
};
struct B : A
{
void f(int)
{
std::cout << "in int" << std::endl;
}
};
int main()
{
A a; a.f(10.10); // as expected, A.f will get called
B b; b.f(10.10); // This calls b.f and we lose the .10 here
return 0;
}
La mia comprensione era quando un tipo viene ereditato, tutti i membri protetti e pubblici saranno accessibili dalla classe derivata. Ma secondo questo esempio, sembra che mi sbagli. Mi aspettavo che b.f chiamasse le classi di base f . Ho ottenuto il risultato atteso cambiando la classe derivata come
struct B : A
{
using A::f;
void f(int)
{
std::cout << "in int" << std::endl;
}
};
Domande
- Perché non funzionava nel primo codice?
- Quale sezione dello standard C ++ descrive tutte queste regole di ambito?
Soluzione
Il primo codice funziona come c ++ è progettato per funzionare.
La risoluzione di sovraccarico segue un insieme di regole molto complicate. Dalla bibbia c ++ di Stroustrup 15.2.2 "[A] le ambiguità tra funzioni di classi di base diverse non vengono risolte in base ai tipi di argomento."
Continua spiegando l'uso di " usando " come hai descritto.
Questa è stata una decisione di progettazione nella lingua.
Tendo a seguire il libro di Stroustrup piuttosto che lo standard, ma sono sicuro che sia lì.
[Modifica]
Eccolo (dallo standard):
Capitolo 13
Quando vengono specificate due o più dichiarazioni diverse per un singolo nome nello stesso ambito, si dice che quel nome sia sovraccarico.
E poi:
13.2 Corrispondenza delle dichiarazioni
1 Le dichiarazioni di due funzioni con lo stesso nome si riferiscono alla stessa funzione se si trovano nello stesso ambito e hanno dichiarazioni di parametri equivalenti (13.1). Un membro di funzione di una classe derivata non è nello stesso ambito di un membro di funzione di lo stesso nome in una classe base.
Altri suggerimenti
È perché A :: f è " nascosto " piuttosto che "sovraccarico" o "sovrascritto". Fare riferimento:
http://www.parashift.com /c++-faq-lite/strange-inheritance.html#faq-23.9
Cerca risoluzione sovraccarico . Una domanda simile, ma non identica .
In C ++, non c'è sovraccarico tra gli ambiti, nell'ambito delle classi derivate non fanno eccezione. (Secondo il linguaggio di programmazione C ++)
Per ulteriori informazioni, consulta http: //www.research. att.com/~bs/bs_faq2.html#overloadderived
Nel primo caso, il metodo della classe base 'f' è nascosto dal metodo della classe derivata. In C ++ non c'è sovraccarico tra gli ambiti; ecco perché non viene chiamato. Lo standard C ++ spiega tutte le regole di ricerca dei nomi dei membri nella sezione 10.2 Ricerca dei nomi dei membri [class.member.lookup] . HTH
La prima versione del codice dovrebbe davvero chiamare B :: f. Si ridefinisce il simbolo "f" in struct "B", in modo da nascondere il simbolo originale "f" da struct "A". Non è un sovraccarico, come potrebbe sembrare.
Ogni volta che il compilatore incontra b.f (), cerca in " B " struct per un simbolo "f". È presente lì, quindi il compilatore decide di chiamare B :: f (int), convertendo double in int. Non vede la necessità di scansionare la classe genitore per una funzione più adatta ...
Tuttavia, quando aggiungi " usando A :: f " è una direttiva explict per il compilatore di scansionare una classe genitrice per il simbolo " f " ;. Ora, la classe B ha due funzioni sovraccaricate: per int e per double.
Credo anche che tu possa scrivere b.A :: f () senza usare " usando " direttiva nell'esempio originale ...