Rozważ ten kod:
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();
}
Błąd kompilatora to:
błąd: „void A :: foo ()” jest prywatne ”.
Ale kiedy usuwam prywatny, po prostu działa. Dlaczego metoda public const nie jest wywoływana, gdy metoda inna niż stała jest prywatna?
Innymi słowy, dlaczego rozwiązanie przeciążenia następuje przed kontrolą dostępu? To jest dziwne. Czy uważasz, że jest to spójne? Mój kod działa, a następnie dodaję metodę, a mój działający kod w ogóle się nie kompiluje.
Odpowiedzi:
Podczas wywołania
a.foo();
kompilator przechodzi przez rozwiązanie przeciążenia, aby znaleźć najlepszą funkcję do użycia. Podczas kompilowania znalezionego zestawu przeciążeńi
Ponieważ
a
tak nie jestconst
, wersja inna niż stała jest najlepszym dopasowaniem, więc kompilator wybieravoid foo()
. Następnie wprowadzane są ograniczenia dostępu i pojawia się błąd kompilatora, ponieważvoid foo()
jest prywatny.Pamiętaj, że w rozwiązaniu problemu z przeciążeniem nie chodzi o „znalezienie najlepszej użytecznej funkcji”. To „znajdź najlepszą funkcję i spróbuj jej użyć”. Jeśli nie może z powodu ograniczeń dostępu lub usunięcia, pojawia się błąd kompilatora.
Cóż, spójrzmy na:
Powiedzmy teraz, że tak naprawdę nie chciałem robić
void foo(Derived * d)
czegoś prywatnego. Gdyby kontrola dostępu pojawiła się jako pierwsza, program ten skompilowałby się, uruchomiłby iBase
zostałby wydrukowany. Może to być bardzo trudne do wyśledzenia w dużej bazie kodu. Ponieważ kontrola dostępu następuje po rozwiązaniu problemu z przeciążeniem, pojawia się miły błąd kompilatora, który mówi mi, że funkcja, którą chcę, aby wywołać, nie może zostać wywołana, a błąd jest znacznie łatwiejszy.źródło
Ostatecznie sprowadza się to do stwierdzenia w standardzie, że dostępność nie powinna być brana pod uwagę podczas rozwiązywania problemu z przeciążeniem . To stwierdzenie można znaleźć w [over.match] klauzuli 3:
a także uwaga w punkcie 1 tej samej sekcji:
Jeśli chodzi o przyczyny, przychodzi mi do głowy kilka możliwych motywacji:
źródło
Załóżmy, że kontrola dostępu nastąpiła przed rozwiązaniem problemu przeciążenia. W rzeczywistości oznaczałoby to
public/protected/private
kontrolowaną widoczność, a nie dostępność.Sekcja 2.10 projektu i ewolucji C ++ autorstwa Stroustrupa zawiera fragment na ten temat, w którym omawia następujący przykład
Stroustrup wspomina, że korzyści wynikające z obowiązujących przepisów (widoczności przed dostępność) jest to, że (tymczasowo) chaning z
private
wnętrzaclass X
dopublic
(na przykład dla celów debugowania) to, że nie ma żadnych zmian cicho w rozumieniu powyższego programu (czyliX::a
próbuje się być dostępne w obu przypadkach, co daje błąd dostępu w powyższym przykładzie). Gdybypublic/protected/private
kontrolował widoczność, znaczenie programu by się zmieniło (w przeciwnym razie globala
zostałby wywołany with ).private
X::a
Następnie stwierdza, że nie przypomina sobie, czy było to spowodowane jawnym projektem, czy efektem ubocznym technologii preprocesora używanej do implementacji C z poprzednikiem Classess standardowego C ++.
Jak to się ma do twojego przykładu? Zasadniczo dlatego, że standardowe rozpoznawanie przeciążenia jest zgodne z ogólną zasadą, że wyszukiwanie nazw ma miejsce przed kontrolą dostępu.
źródło
Ponieważ niejawny
this
wskaźnik jestconst
inny niż -, kompilator najpierw sprawdzi obecność nie-const
wersji funkcji przedconst
wersją.Jeśli wyraźnie zaznaczyć nieprzestrzegania
const
jednegoprivate
następnie uchwała nie powiedzie się, a kompilator nie będzie kontynuować poszukiwania.źródło
Ważne jest, aby pamiętać o kolejności rzeczy, które się dzieją, czyli:
delete
d), zakończy się niepowodzeniem.(3) dzieje się po (2). Co jest naprawdę ważne, ponieważ w przeciwnym razie tworzenie funkcji
delete
d lubprivate
stałoby się w pewnym sensie bez znaczenia i znacznie trudniejsze do rozważenia.W tym przypadku:
A::foo()
iA::foo() const
.A::foo()
że ta ostatnia obejmuje konwersję kwalifikacji na niejawnymthis
argumencie.A::foo()
jestprivate
i nie masz do niego dostępu, stąd kod jest źle sformułowany.źródło
Sprowadza się to do dość podstawowej decyzji projektowej w C ++.
Podczas wyszukiwania funkcji w celu spełnienia wywołania kompilator przeprowadza takie wyszukiwanie:
Przeszukuje znaleźć pierwsze 1 zakres, w którym istnieje coś o tej nazwie.
Kompilator znajduje wszystkie funkcje (lub funktory itp.) O tej nazwie w tym zakresie.
Następnie kompilator rozwiązuje przeciążenie, aby znaleźć najlepszego kandydata spośród znalezionych (niezależnie od tego, czy są dostępne, czy nie).
Na koniec kompilator sprawdza, czy wybrana funkcja jest dostępna.
Z powodu tej kolejności tak, możliwe jest, że kompilator wybierze przeciążenie, które jest niedostępne, mimo że istnieje inne przeciążenie, które jest dostępne (ale nie zostało wybrane podczas rozwiązywania przeciążenia).
Czy byłoby możliwe , aby robić rzeczy inaczej: tak, to niewątpliwie możliwe. Z pewnością doprowadziłoby to jednak do całkiem innego języka niż C ++. Okazuje się, że wiele pozornie niewielkich decyzji może mieć konsekwencje, które mają znacznie większy wpływ, niż mogłoby się początkowo wydawać oczywiste.
źródło
Kontrola dostępu (
public
,protected
,private
) nie wpływają na przeciążenie rozdzielczość. Kompilator wybiera,void foo()
ponieważ jest to najlepsze dopasowanie. Fakt, że jest niedostępny, tego nie zmienia. Usunięcie go pozostawia tylkovoid foo() const
, co jest wtedy najlepszym (tj. Jedynym) dopasowaniem.źródło
W tym wezwaniu:
W każdej funkcji składowej zawsze dostępny jest niejawny
this
wskaźnik. Aconst
kwalifikacjathis
pochodzi z wywołującego odniesienia / obiektu. Powyższe wywołanie jest traktowane przez kompilator jako:Ale masz dwie deklaracje,
A::foo
które są traktowane jak :Dzięki rozwiązaniu przeciążenia pierwsza zostanie wybrana jako inna niż stała
this
, a druga zostanie wybrana dlaconst this
. Jeśli usuniesz pierwszy, drugi połączy się z obydwomaconst
inon-const
this
.Po rozwiązaniu problemu z przeciążeniem, aby wybrać najlepszą wykonalną funkcję, następuje kontrola dostępu. Ponieważ określono dostęp do wybranego przeciążenia jako
private
, kompilator będzie narzekać.Norma mówi tak:
Ale jeśli to zrobisz:
Wtedy
const
pasuje tylko przeciążenie.źródło
Na powód techniczny odpowiedziały inne odpowiedzi. Skoncentruję się tylko na tym pytaniu:
Tak zaprojektowano język. Celem jest próba wywołania najlepszego możliwego przeciążenia, tak dalece, jak to możliwe. Jeśli to się nie powiedzie, zostanie wyzwolony błąd, aby przypomnieć o ponownym rozważeniu projektu.
Z drugiej strony załóżmy, że Twój kod został skompilowany i działał dobrze z
const
wywoływaną funkcją składową. Któregoś dnia ktoś (może Ty) zdecyduje się zmienić dostępność funkcji niebędącejconst
członkiem zprivate
napublic
. Wtedy zachowanie zmieniłoby się bez błędów kompilacji! To byłaby niespodzianka .źródło
Ponieważ zmienna
a
wmain
funkcji nie jest zadeklarowana jakoconst
.Stałe funkcje składowe są wywoływane na stałych obiektach.
źródło
Specyfikatory dostępu nigdy nie wpływają na wyszukiwanie nazw i rozpoznawanie wywołań funkcji. Funkcja jest wybierana zanim kompilator sprawdzi, czy wywołanie powinno wywołać naruszenie dostępu.
W ten sposób, jeśli zmienisz specyfikator dostępu, zostaniesz ostrzeżony w czasie kompilacji, jeśli nastąpi naruszenie w istniejącym kodzie; Gdyby wziąć pod uwagę prywatność przy rozwiązywaniu wywołań funkcji, zachowanie programu mogłoby się po cichu zmienić.
źródło