Est-ce que la suppression d'un cache de méthode virtuelle non-const une surcharge const?
-
08-10-2019 - |
Question
Considérez:
#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
}
(j'utilise GCC.)
Il semble donc que la version const de f () qui est caché en C. Cela fait beaucoup de sens pour moi, mais est-il mandaté par la norme?
La solution
Je (encore une fois) un lien entre ce grand article :
regardeD'abord, [le compilateur] dans le champ d'application immédiate, dans ce cas, la portée de la classe C, et fait une liste des toutes les fonctions qu'il peut trouver que sont nommé f (indépendamment du fait qu'ils sont accessible ou même prendre le droit nombre de paramètres). Seulement si ne continue-t-il alors « Vers l'extérieur » dans la prochaine enfermant portée [...]
Alors oui, la version const
de f
est caché, et qui est tout à fait normal. Comme l'a souligné Simone, vous pouvez utiliser une instruction using
pour apporter A::f
portée de C
.
Autres conseils
Oui, il est. Vous pouvez écrire:
struct C : public A {
virtual void f() { cout << "C::f" << endl; }
using A::f;
};
pour faire votre compilation de code:
int main()
{
const B b;
b.f(); // prints "A::f const"
const C c;
c.f(); // prints "A::f const"
}
Pour plus d'infos, vous pouvez consulter le projet de document de 2010 C ++ (que vous pouvez trouver ) chapitre 10.2. (3-4).
Il est le virtuality ou const-ness (ou son absence) qui cache les membres de base, toute méthode dérivés cache une méthode de base du même nom. Cela a été fait pour améliorer le problème fragile de la classe de base.
Imaginez votre code fonctionnait (peut-être pendant des années) comme ci-dessous, avec des parties non pertinentes supprimées:
struct Base {
};
struct Derived : Base {
void f(double);
}
void g(Derived &d) {
d.f(42);
}
Ensuite, vous devez modifier la base pour inclure une méthode qui fait quelque chose de complètement différent, mais, pour une raison quelconque, vous voulez le nommer « f »:
struct Base {
void f(int);
};
Sans cette règle, tous l'utilisation d'un dérivé d'appel f doit être évaluée manuellement - et si la base est dans une bibliothèque donnée à d'autres personnes, vous ne pouvez pas même avoir accès à ces autres utilisations! Il y a pire en face des conversions (implicites) définies par l'utilisateur.
Au lieu de cela, il a été décidé d'exiger des classes dérivées de dire explicitement qu'ils veulent importer des noms donnés de base avec une déclaration à l'aide. Cette règle peut être surprenant et je ne suis pas sûr que c'est un avantage net pour la langue d'aujourd'hui, mais ils ne me demandez pas - à l'époque, je ne pouvais probablement leur ai répondu avec deux syllabes des mots, de toute façon. :)
Insérer using B::f;
struct C : public A {
using A::f;
virtual void f() { cout << "C::f" << endl; }
};
C ++ standard 2003. 13.2 p.1:
Deux déclarations de fonction du même nom se réfèrent à la même fonction si elles sont dans la même portée et ont déclarations de paramètres équivalents (13.1). Une fonction membre d'une classe dérivée n'est pas le même champ d'application en tant que membre de la fonction du même nom dans une classe de base.
Ainsi C::f
cache tout A::f
.