Fa l'override un metodo non-const nascondere virtuale sovraccarico const?
-
08-10-2019 - |
Domanda
Si consideri:
#include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "A::f" << endl; }
virtual void f() const { cout << "A::f const" << endl; }
};
struct B : public A {};
struct C : public A {
virtual void f() { cout << "C::f" << endl; }
};
int main()
{
const B b;
b.f(); // prints "A::f const"
const C c;
c.f();
// Compile-time error: passing ‘const C’ as ‘this’ argument of
// ‘virtual void C::f()’ discards qualifiers
}
(sto usando GCC.)
Così sembra che la versione const di f () viene nascosto in C. Questo rende un sacco di senso per me, ma è mandato dalla norma?
Soluzione
io (ancora una volta) collegare questo grande articolo :
In primo luogo, [il compilatore] guarda allo applicazione immediata, in questo caso la ambito di classe C, e fa un elenco di tutte le funzioni si può trovare che sono chiamato f (a prescindere dal fatto che siano accessibili o addirittura prendere la destra numero di parametri). Solo se non lo fa poi continuare "Passivo" nel prossimo racchiude portata ??strong> [...]
Quindi sì, la versione const
di f
è nascosto, e questo è perfettamente normale. Come sottolineato da Simone, è possibile utilizzare un'istruzione using
per portare A::f
portata C
.
Altri suggerimenti
Sì, lo è. Si può scrivere:
struct C : public A {
virtual void f() { cout << "C::f" << endl; }
using A::f;
};
per rendere il codice di compilazione:
int main()
{
const B b;
b.f(); // prints "A::f const"
const C c;
c.f(); // prints "A::f const"
}
Per maggiori informazioni, è possibile fare riferimento al 2010, C ++ bozza del documento (che potete trovare qui ) capitolo 10.2. (3-4).
Non è la virtualità o const-ness (o mancanza) che nasconde l'elemento di base, qualsiasi metodo nasconde derivato un metodo base dello stesso nome. Ciò è stato fatto per migliorare la fragile problema della classe base.
Immaginate il vostro codice funzionava (forse per anni), come di seguito, con parti non rilevanti rimosso:
struct Base {
};
struct Derived : Base {
void f(double);
}
void g(Derived &d) {
d.f(42);
}
Quindi è necessario modificare Base per includere un metodo che fa qualcosa di completamente diverso, ma, per qualche ragione, si vuole chiamarla 'f':
struct Base {
void f(int);
};
Senza questa regola, tutti uso di un derivato chiamare f deve essere valutato manualmente - e se la base è in una libreria data ad altre persone, si può anche non avere accesso a tali altri usi! C'è di peggio di fronte alle conversioni definite dall'utente (impliciti).
Al contrario, si è deciso di richiedere classi derivate di affermare esplicitamente che vogliono importare i dati nomi di base con una dichiarazione secondo. Questa regola può essere sorprendente e non sono sicuro che sia un beneficio netto per il linguaggio di oggi, ma non mi ha chiesto - a quel tempo, avrei potuto probabilmente li ho risposto solo con le parole di due sillabe, in ogni caso. :)
Inserisci using B::f;
struct C : public A {
using A::f;
virtual void f() { cout << "C::f" << endl; }
};
C ++ standard 2003. 13.2 p.1:
Due dichiarazioni di funzione con lo stesso nome si riferiscono alla stessa funzione se sono nella stessa portata e avere dichiarazioni dei parametri equivalenti (13.1). Una funzione membro di una classe derivata non in lo stesso portata come membro funzione con lo stesso nome in una classe di base.
pelli Così C::f
tutti A::f
.