Betrachten Sie diesen 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();
}
Der Compilerfehler ist:
Fehler: 'void A :: foo ()' ist privat`.
Aber wenn ich die private lösche, funktioniert es einfach. Warum wird die public const-Methode nicht aufgerufen, wenn die non-const-Methode privat ist?
Mit anderen Worten, warum kommt die Überlastungsauflösung vor der Zugriffskontrolle? Das ist merkwürdig. Denken Sie, dass es konsequent ist? Mein Code funktioniert und dann füge ich eine Methode hinzu, und mein Arbeitscode wird überhaupt nicht kompiliert.
Antworten:
Wenn Sie aufrufen
a.foo();
, durchläuft der Compiler die Überlastungsauflösung, um die beste zu verwendende Funktion zu finden. Wenn es die Überlastmenge erstellt, findet esund
Da dies
a
nichtconst
der Fall ist , ist die nicht konstante Version die beste Übereinstimmung, daher wählt der Compilervoid foo()
. Dann werden die Zugriffsbeschränkungen eingeführt und Sie erhalten einen Compilerfehler, da dieservoid foo()
privat ist.Denken Sie daran, dass es bei der Überlastungsauflösung nicht darum geht, die beste verwendbare Funktion zu finden. Es ist 'finde die beste Funktion und versuche sie zu benutzen'. Wenn dies aufgrund von Zugriffsbeschränkungen oder Löschen nicht möglich ist, wird ein Compilerfehler angezeigt.
Schauen wir uns an:
Nehmen wir jetzt an, ich wollte eigentlich nicht
void foo(Derived * d)
privat machen . Wenn die Zugriffskontrolle an erster Stelle steht, wird dieses Programm kompiliert und ausgeführt undBase
gedruckt. Dies kann in einer großen Codebasis sehr schwer zu finden sein. Da die Zugriffskontrolle nach der Überlastungsauflösung erfolgt, wird ein netter Compilerfehler angezeigt, der mir mitteilt, dass die Funktion, die aufgerufen werden soll, nicht aufgerufen werden kann, und ich kann den Fehler viel einfacher finden.quelle
Letztendlich ist dies auf die Behauptung im Standard zurückzuführen, dass die Barrierefreiheit bei der Durchführung der Überlastungsauflösung nicht berücksichtigt werden sollte . Diese Behauptung kann in [over.match] Abschnitt 3 gefunden werden:
und auch die Anmerkung in Abschnitt 1 desselben Abschnitts:
Was den Grund betrifft, kann ich mir einige mögliche Motivationen vorstellen:
quelle
Angenommen, die Zugriffskontrolle erfolgte vor der Überlastungsauflösung. Tatsächlich würde dies bedeuten, dass
public/protected/private
die Sichtbarkeit und nicht die Zugänglichkeit kontrolliert wird.Abschnitt 2.10 von Design und Evolution von C ++ von Stroustrup enthält eine Passage, in der er das folgende Beispiel erläutert
Stroustrup erwähnt , dass ein Vorteil der geltenden Regeln (Sichtbarkeit vor Zugänglichkeit) , die (vorübergehend) die chaning
private
innenclass X
inpublic
(zB für die Zwecke des Debugging) ist , dass es im Sinne des oben genannten Programms keine ruhige Änderung (dhX::a
wird versucht, in beiden Fällen zugegriffen werden, was im obigen Beispiel einen Zugriffsfehler ergibt). Wennpublic/protected/private
die Sichtbarkeit gesteuert würde, würde sich die Bedeutung des Programms ändern ( andernfalls würde globala
aufgerufen ).private
X::a
Er gibt dann an, dass er sich nicht daran erinnert, ob es sich um ein explizites Design oder einen Nebeneffekt der Präprozessortechnologie handelte, die zur Implementierung des C mit Classess-Vorgänger von Standard C ++ verwendet wurde.
Wie hängt das mit Ihrem Beispiel zusammen? Grundsätzlich, weil die vom Standard vorgenommene Überlastungsauflösung der allgemeinen Regel entspricht, dass die Namenssuche vor der Zugriffskontrolle erfolgt.
quelle
Da der implizite
this
Zeiger nicht istconst
, prüft der Compilerconst
vor einerconst
Version zunächst, ob eine Nichtversion der Funktion vorhanden ist .Wenn Sie sich ausdrücklich nicht markieren
const
ein ,private
dann wird die Auflösung fehl und der Compiler wird nicht weitersuchen.quelle
Es ist wichtig, die Reihenfolge der Ereignisse zu berücksichtigen:
delete
d), schlagen Sie fehl.(3) geschieht nach (2). Was wirklich wichtig ist, weil sonst das Erstellen von Funktionen
delete
d oderprivate
bedeutungslos und viel schwieriger zu überlegen wäre.In diesem Fall:
A::foo()
undA::foo() const
.A::foo()
dass letztere eine Qualifizierungskonvertierung für das implizitethis
Argument beinhaltet.A::foo()
istprivate
und Sie haben keinen Zugriff darauf, daher ist der Code schlecht geformt.quelle
Dies hängt von einer ziemlich grundlegenden Entwurfsentscheidung in C ++ ab.
Wenn der Compiler nach der Funktion sucht, um einen Aufruf zu erfüllen, führt er eine Suche wie folgt durch:
Es wird nach dem ersten finden 1 Umfang , an das es etwas mit diesem Namen.
Der Compiler findet alle Funktionen (oder Funktoren usw.) mit diesem Namen in diesem Bereich.
Anschließend führt der Compiler eine Überlastungsauflösung durch, um den besten Kandidaten unter den gefundenen zu finden (unabhängig davon, ob auf sie zugegriffen werden kann oder nicht).
Schließlich prüft der Compiler, ob auf diese ausgewählte Funktion zugegriffen werden kann.
Aufgrund dieser Reihenfolge ist es möglich, dass der Compiler eine Überladung auswählt, auf die nicht zugegriffen werden kann, obwohl eine andere Überladung verfügbar ist (die jedoch während der Überlastungsauflösung nicht ausgewählt wird).
Ob es möglich wäre, Dinge anders zu machen: Ja, das ist zweifellos möglich. Es würde definitiv zu einer ganz anderen Sprache als C ++ führen. Es stellt sich heraus, dass viele scheinbar eher geringfügige Entscheidungen Auswirkungen haben können, die viel mehr betreffen, als zunächst offensichtlich sein könnte.
quelle
Zugangskontrollen (
public
,protected
,private
) haben keinen Einfluss auf die Überladungsauflösung. Der Compiler wählt,void foo()
weil es die beste Übereinstimmung ist. Die Tatsache, dass es nicht zugänglich ist, ändert daran nichts. Wenn Sie es entfernen, bleibt nurvoid foo() const
das übrig , was dann die beste (dh einzige) Übereinstimmung ist.quelle
In diesem Aufruf:
this
In jeder Mitgliedsfunktion ist immer ein impliziter Zeiger verfügbar. Und dieconst
Qualifikation vonthis
wird der aufrufenden Referenz / dem aufrufenden Objekt entnommen. Der obige Aufruf behandelt wird vom Compiler als:Sie haben jedoch zwei Erklärungen,
A::foo
die wie folgt behandelt werden :Durch die Überlastungsauflösung wird die erste für Nicht-Konstante ausgewählt
this
, die zweite für aconst this
. Wenn Sie die erste entfernen, wird die zweite an beideconst
und gebundennon-const
this
.Nach der Überlastungsauflösung zur Auswahl der bestmöglichen Funktion erfolgt die Zugriffskontrolle. Da Sie den Zugriff auf die ausgewählte Überladung als angegeben haben
private
, beschwert sich der Compiler.Der Standard sagt es so:
Aber wenn Sie dies tun:
Dann wird nur die
const
Überlast angepasst.quelle
Der technische Grund wurde durch andere Antworten beantwortet. Ich werde mich nur auf diese Frage konzentrieren:
So wurde die Sprache gestaltet. Die Absicht ist es, so weit wie möglich die bestmögliche Überlastung zu nennen. Wenn dies fehlschlägt, wird ein Fehler ausgelöst, der Sie daran erinnert, das Design erneut zu prüfen.
Angenommen, Ihr Code wurde kompiliert und funktioniert gut mit der
const
aufgerufenen Member-Funktion. Eines Tages beschließt dann jemand (vielleicht Sie selbst), die Zugänglichkeit der Nichtmitgliedsfunktionconst
vonprivate
auf zu ändernpublic
. Dann würde sich das Verhalten ohne Kompilierungsfehler ändern! Das wäre eine Überraschung .quelle
Weil die Variable
a
in dermain
Funktion nicht als deklariert istconst
.Konstante Elementfunktionen werden für konstante Objekte aufgerufen.
quelle
Zugriffsspezifizierer haben niemals Einfluss auf die Namenssuche und die Auflösung von Funktionsaufrufen. Die Funktion wird ausgewählt, bevor der Compiler prüft, ob der Aufruf eine Zugriffsverletzung auslösen soll.
Auf diese Weise werden Sie beim Ändern eines Zugriffsspezifizierers beim Kompilieren benachrichtigt, wenn ein Verstoß gegen den vorhandenen Code vorliegt. Wenn die Privatsphäre bei der Auflösung von Funktionsaufrufen berücksichtigt würde, könnte sich das Verhalten Ihres Programms stillschweigend ändern.
quelle