Considérez ce code:
struct A
{
void foo() const
{
std::cout << "const" << std::endl;
}
private:
void foo()
{
std::cout << "non - const" << std::endl;
}
};
int main()
{
A a;
a.foo();
}
L'erreur du compilateur est:
erreur: 'void A :: foo ()' est privé`.
Mais quand je supprime le privé, cela fonctionne. Pourquoi la méthode public const n'est-elle pas appelée lorsque la méthode non-const est privée?
En d'autres termes, pourquoi la résolution des surcharges passe-t-elle avant le contrôle d'accès? Cela est étrange. Pensez-vous que c'est cohérent? Mon code fonctionne, puis j'ajoute une méthode, et mon code de travail ne se compile pas du tout.
Réponses:
Lorsque vous appelez
a.foo();
, le compilateur passe par la résolution de surcharge pour trouver la meilleure fonction à utiliser. Lorsqu'il crée l'ensemble de surcharge qu'il trouveet
Maintenant, puisque ce
a
n'est pas le casconst
, la version non-const est la meilleure correspondance, donc le compilateur choisitvoid foo()
. Ensuite, les restrictions d'accès sont mises en place et vous obtenez une erreur de compilation, carvoid foo()
est privé.Souvenez-vous qu'en résolution de surcharge, il ne s'agit pas de «trouver la meilleure fonction utilisable». C'est «trouver la meilleure fonction et essayer de l'utiliser». Si ce n'est pas le cas en raison de restrictions d'accès ou en cours de suppression, vous obtenez une erreur du compilateur.
Eh bien, regardons:
Maintenant, disons que je ne voulais pas vraiment rendre
void foo(Derived * d)
privé. Si le contrôle d'accès venait en premier, ce programme se compilerait et s'exécuterait etBase
serait imprimé. Cela pourrait être très difficile à retrouver dans une grande base de code. Étant donné que le contrôle d'accès vient après la résolution de la surcharge, j'obtiens une belle erreur de compilation me disant que la fonction que je veux appeler ne peut pas être appelée, et je peux trouver le bogue beaucoup plus facilement.la source
En fin de compte, cela revient à l'affirmation de la norme selon laquelle l' accessibilité ne doit pas être prise en compte lors de la résolution de surcharge . Cette assertion peut être trouvée dans la clause 3 [over.match] :
et aussi la note dans la clause 1 de la même section:
Quant à savoir pourquoi, je peux penser à quelques motivations possibles:
la source
Supposons que le contrôle d'accès vienne avant la résolution de surcharge. En fait, cela signifierait
public/protected/private
une visibilité contrôlée plutôt qu'une accessibilité.La section 2.10 de Design and Evolution of C ++ par Stroustrup a un passage à ce sujet où il discute de l'exemple suivant
Stroustrup mentionne qu'un avantage des règles actuelles (visibilité avant l' accessibilité) est que (temporairement) chaning l'
private
intérieurclass X
enpublic
(par exemple aux fins de débogage) est qu'il n'y a pas de changement de calme au sens du programme ci - dessus (X::a
on tente de accessible dans les deux cas, ce qui donne une erreur d'accès dans l'exemple ci-dessus). Sipublic/protected/private
contrôlerait la visibilité, la signification du programme changerait (globala
serait appelé avecprivate
, sinonX::a
).Il déclare ensuite qu'il ne se souvient pas si c'était par une conception explicite ou un effet secondaire de la technologie de préprocesseur utilisée pour implémenter le C avec le prédécesseur Classess du C ++ standard.
Comment cela est-il lié à votre exemple? Fondamentalement, parce que la norme a rendu la résolution de surcharge conforme à la règle générale selon laquelle la recherche de nom vient avant le contrôle d'accès.
la source
Puisque le
this
pointeur implicite est non-const
, le compilateur vérifiera d'abord la présence d'une non-const
version de la fonction avant uneconst
version.Si vous marquez explicitement le non-
const
un,private
la résolution échouera et le compilateur ne poursuivra pas la recherche.la source
Il est important de garder à l'esprit l'ordre des choses qui se produisent, à savoir:
delete
d), échouez.(3) se produit après (2). Ce qui est vraiment important, car sinon, rendre les fonctions
delete
d ouprivate
deviendrait un peu dénué de sens et beaucoup plus difficile à raisonner.Dans ce cas:
A::foo()
etA::foo() const
.A::foo()
que cette dernière implique une conversion de qualification sur l'this
argument implicite .A::foo()
c'estprivate
et vous n'y avez pas accès, donc le code est mal formé.la source
Cela revient à une décision de conception assez basique en C ++.
Lors de la recherche de la fonction pour satisfaire un appel, le compilateur effectue une recherche comme celle-ci:
Il cherche à trouver la première 1 portée à laquelle il y a quelque chose avec ce nom.
Le compilateur trouve toutes les fonctions (ou foncteurs, etc.) avec ce nom dans cette portée.
Ensuite, le compilateur surcharge la résolution pour trouver le meilleur candidat parmi ceux qu'il a trouvés (qu'ils soient accessibles ou non).
Enfin, le compilateur vérifie si cette fonction choisie est accessible.
En raison de cet ordre, oui, il est possible que le compilateur choisisse une surcharge qui n'est pas accessible, même s'il existe une autre surcharge qui est accessible (mais non choisie lors de la résolution de surcharge).
Quant à savoir s'il serait possible de faire les choses différemment: oui, c'est sans aucun doute possible. Cela conduirait certainement à un langage assez différent de celui du C ++. Il s'avère que beaucoup de décisions en apparence plutôt mineures peuvent avoir des ramifications qui affectent beaucoup plus qu'il ne semble initialement évident.
la source
Contrôles d'accès (
public
,protected
,private
) ne touchent pas la surcharge résolution. Le compilateur choisitvoid foo()
parce que c'est la meilleure correspondance. Le fait qu'il ne soit pas accessible n'y change rien. Le supprimer laisse seulementvoid foo() const
, ce qui est alors la meilleure correspondance (c'est-à-dire la seule).la source
Dans cet appel:
Il y a toujours un
this
pointeur implicite disponible dans chaque fonction membre. Et laconst
qualification dethis
est tirée de la référence / objet appelant. L'appel ci-dessus est traité par le compilateur comme:Mais vous avez deux déclarations
A::foo
dont on traite comme :Par résolution de surcharge, le premier sera sélectionné pour non-const
this
, le second sera sélectionné pour aconst this
. Si vous supprimez le premier, le second se liera à la foisconst
etnon-const
this
.Après la résolution de surcharge pour sélectionner la meilleure fonction viable, vient le contrôle d'accès. Puisque vous avez spécifié l'accès à la surcharge choisie comme
private
, le compilateur se plaindra alors.La norme le dit:
Mais si vous faites ceci:
Ensuite, seule la
const
surcharge sera adaptée.la source
La raison technique a été répondue par d'autres réponses. Je me concentrerai uniquement sur cette question:
C'est ainsi que le langage a été conçu. L'intention est d'essayer d'appeler la meilleure surcharge viable, dans la mesure du possible. En cas d'échec, une erreur sera déclenchée pour vous rappeler de revoir la conception.
D'un autre côté, supposons que votre code soit compilé et fonctionne correctement avec la
const
fonction membre appelée. Un jour, quelqu'un (peut-être vous-même) décide alors de changer l'accessibilité de laconst
fonction non- membre deprivate
àpublic
. Ensuite, le comportement changerait sans aucune erreur de compilation! Ce serait une surprise .la source
Parce que la variable
a
dans lamain
fonction n'est pas déclarée commeconst
.Les fonctions membres constantes sont appelées sur des objets constants.
la source
Les spécificateurs d'accès n'affectent jamais la recherche de nom et la résolution des appels de fonction. La fonction est sélectionnée avant que le compilateur vérifie si l'appel doit déclencher une violation d'accès.
De cette façon, si vous modifiez un spécificateur d'accès, vous serez alerté au moment de la compilation s'il y a une violation dans le code existant; si la confidentialité était prise en compte pour la résolution des appels de fonction, le comportement de votre programme pourrait changer silencieusement.
la source