Pertimbangkan kode ini:
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();
}
Kesalahan kompilernya adalah:
kesalahan: 'void A :: foo ()' bersifat pribadi`.
Tetapi ketika saya menghapus yang pribadi, itu hanya berfungsi. Mengapa metode public const tidak dipanggil ketika non-const private?
Dengan kata lain, mengapa resolusi kelebihan beban harus ada sebelum kontrol akses? Ini aneh. Apakah menurut Anda itu konsisten? Kode saya berfungsi dan kemudian saya menambahkan metode, dan kode kerja saya tidak dapat dikompilasi sama sekali.
Jawaban:
Saat Anda memanggil
a.foo();
, kompilator melewati resolusi kelebihan beban untuk menemukan fungsi terbaik untuk digunakan. Saat membangun kumpulan kelebihan yang ditemukannyadan
Sekarang, karena
a
tidakconst
, versi non-const adalah yang paling cocok, jadi kompilator memilihvoid foo()
. Kemudian pembatasan akses diberlakukan dan Anda mendapatkan kesalahan kompiler, karenavoid foo()
bersifat pribadi.Ingat, dalam resolusi kelebihan beban, ini bukan 'temukan fungsi terbaik yang dapat digunakan'. Ini adalah 'temukan fungsi terbaik dan coba gunakan'. Jika tidak bisa karena pembatasan akses atau sedang dihapus, maka Anda mendapatkan kesalahan kompiler.
Nah, mari kita simak:
Sekarang katakanlah saya sebenarnya tidak bermaksud menjadikan
void foo(Derived * d)
pribadi. Jika kontrol akses didahulukan, maka program ini akan dikompilasi dan dijalankan danBase
akan dicetak. Ini bisa sangat sulit untuk dilacak dalam basis kode yang besar. Karena kontrol akses muncul setelah resolusi kelebihan beban, saya mendapatkan kesalahan kompiler yang bagus yang memberi tahu saya bahwa fungsi yang saya ingin panggil tidak dapat dipanggil, dan saya dapat menemukan bug dengan lebih mudah.sumber
Pada akhirnya, hal ini bermuara pada pernyataan dalam standar bahwa aksesibilitas tidak boleh dipertimbangkan saat melakukan resolusi kelebihan beban . Penegasan ini dapat ditemukan di [over.match] klausa 3:
dan juga Catatan di klausul 1 dari bagian yang sama:
Untuk alasannya, saya dapat memikirkan beberapa kemungkinan motivasi:
sumber
Misalkan kontrol akses datang sebelum resolusi yang berlebihan. Secara efektif, ini berarti
public/protected/private
visibilitas terkontrol daripada aksesibilitas.Bagian 2.10 dari Desain dan Evolusi C ++ oleh Stroustrup memiliki bagian tentang ini di mana dia membahas contoh berikut
Stroustrup menyebutkan bahwa keuntungan dari aturan saat ini (visibilitas sebelum aksesibilitas) adalah bahwa (sementara) mengubah bagian
private
dalamnyaclass X
menjadipublic
(misalnya untuk tujuan debugging) adalah bahwa tidak ada perubahan diam-diam dalam arti dari program di atas (yaituX::a
mencoba untuk diakses dalam kedua kasus, yang memberikan kesalahan akses pada contoh di atas). Jikapublic/protected/private
akan mengontrol visibilitas, arti program akan berubah (globala
akan disebut denganprivate
, jika tidakX::a
).Dia kemudian menyatakan bahwa dia tidak ingat apakah itu dengan desain eksplisit atau efek samping dari teknologi preprocessor yang digunakan untuk mengimplementasikan C dengan pendahulu Classess ke Standar C ++.
Bagaimana ini terkait dengan teladan Anda? Pada dasarnya karena Standar membuat resolusi kelebihan beban sesuai dengan aturan umum bahwa pencarian nama dilakukan sebelum kontrol akses.
sumber
Karena
this
pointer implisit adalah non-const
, kompilator pertama-tama akan memeriksa keberadaan fungsi bukanconst
versi sebelumconst
versi.Jika Anda secara eksplisit menandai non-
const
oneprivate
maka resolusi akan gagal, dan kompilator tidak akan melanjutkan pencarian.sumber
Penting untuk diingat urutan hal-hal yang terjadi, yaitu:
delete
d), gagal.(3) terjadi setelah (2). Yang sangat penting, karena jika tidak membuat fungsi
delete
d atauprivate
akan menjadi semacam tidak berarti dan jauh lebih sulit untuk dipikirkan.Pada kasus ini:
A::foo()
danA::foo() const
.A::foo()
karena yang terakhir melibatkan konversi kualifikasi padathis
argumen implisit .A::foo()
adalahprivate
dan Anda tidak memiliki akses ke sana, oleh karena itu kodenya salah bentuk.sumber
Ini tergantung pada keputusan desain yang cukup mendasar di C ++.
Saat mencari fungsi untuk memenuhi panggilan, kompilator melakukan pencarian seperti ini:
Ini mencari untuk menemukan 1 lingkup pertama di mana ada sesuatu dengan nama itu.
Kompilator menemukan semua fungsi (atau functor, dll.) Dengan nama itu dalam cakupan itu.
Kemudian kompilator melakukan overload resolusi untuk menemukan kandidat terbaik di antara mereka yang ditemukan (apakah mereka dapat diakses atau tidak).
Akhirnya, kompilator memeriksa apakah fungsi yang dipilih itu dapat diakses.
Karena pengurutan itu, ya, mungkin saja compiler akan memilih kelebihan muatan yang tidak dapat diakses, meskipun ada kelebihan muatan lain yang dapat diakses (tetapi tidak dipilih selama resolusi kelebihan muatan).
Mengenai apakah mungkin melakukan sesuatu secara berbeda: ya, tidak diragukan lagi itu mungkin. Ini pasti akan mengarah ke bahasa yang sangat berbeda dari C ++. Ternyata banyak keputusan yang tampaknya kecil dapat memiliki konsekuensi yang mempengaruhi lebih banyak daripada yang mungkin terlihat pada awalnya.
sumber
Kontrol akses (
public
,protected
,private
) tidak mempengaruhi berlebihan resolusi. Kompilator memilihvoid foo()
karena paling cocok. Fakta bahwa itu tidak dapat diakses tidak mengubah itu. Membuangnya hanya menyisakanvoid foo() const
, yang kemudian merupakan yang terbaik (yaitu, hanya) yang cocok.sumber
Dalam panggilan ini:
Selalu ada
this
penunjuk implisit yang tersedia di setiap fungsi anggota. Danconst
kualifikasithis
diambil dari referensi / objek pemanggil. Panggilan di atas diperlakukan oleh kompilator sebagai:Tetapi Anda memiliki dua deklarasi
A::foo
yang diperlakukan seperti :Dengan membebani resolusi, yang pertama akan dipilih untuk non-const
this
, yang kedua akan dipilih untuk aconst this
. Jika Anda menghapus yang pertama, yang kedua akan mengikat keduanyaconst
dannon-const
this
.Setelah resolusi berlebih untuk memilih fungsi terbaik yang layak, datanglah kontrol akses. Karena Anda menentukan akses ke overload yang dipilih sebagai
private
, kompilator akan mengeluh.Standar mengatakan demikian:
Tetapi jika Anda melakukan ini:
Kemudian, hanya
const
kelebihan beban yang akan cocok.sumber
Alasan teknis telah dijawab oleh jawaban lain. Saya hanya akan fokus pada pertanyaan ini:
Begitulah cara bahasa itu dirancang. Maksudnya adalah mencoba memanggil overload yang layak terbaik, sejauh mungkin. Jika gagal, kesalahan akan muncul untuk mengingatkan Anda untuk mempertimbangkan desain lagi.
Di sisi lain, misalkan kode Anda dikompilasi dan bekerja dengan baik dengan
const
fungsi anggota yang sedang dipanggil. Suatu saat, seseorang (mungkin diri Anda sendiri) kemudian memutuskan untuk mengubah aksesibilitas fungsi non-const
anggota dariprivate
menjadipublic
. Kemudian, perilaku akan berubah tanpa kesalahan kompilasi! Ini akan menjadi kejutan .sumber
Karena variabel
a
dalammain
fungsi tidak dideklarasikan sebagaiconst
.Fungsi anggota konstan dipanggil pada objek konstan.
sumber
Penentu akses tidak pernah memengaruhi pencarian nama dan resolusi panggilan fungsi. Fungsi ini dipilih sebelum kompilator memeriksa apakah panggilan tersebut harus memicu pelanggaran akses.
Dengan cara ini, jika Anda mengubah penentu akses, Anda akan diberi tahu pada waktu kompilasi jika ada pelanggaran dalam kode yang ada; jika privasi diperhitungkan untuk resolusi panggilan fungsi, perilaku program Anda dapat berubah secara diam-diam.
sumber