Ini adalah quirk / fitur di C ++. Meskipun kami tidak menganggap referensi sebagai tipe, mereka sebenarnya "duduk" dalam sistem tipe. Meskipun ini tampak janggal (mengingat bahwa ketika referensi digunakan, semantik referensi terjadi secara otomatis dan referensi "disingkirkan"), ada beberapa alasan yang dapat dipertahankan mengapa referensi dimodelkan dalam sistem tipe alih-alih sebagai atribut terpisah di luar Tipe.
Pertama, mari kita pertimbangkan bahwa tidak setiap atribut dari nama yang dideklarasikan harus dalam sistem tipe. Dari bahasa C, kami memiliki "kelas penyimpanan", dan "hubungan". Sebuah nama dapat diperkenalkan sebagai extern const int ri
, di mana extern
menunjukkan kelas penyimpanan statis dan adanya linkage. Jenisnya adil const int
.
C ++ jelas merangkul gagasan bahwa ekspresi memiliki atribut yang berada di luar sistem tipe. Bahasa tersebut sekarang memiliki konsep "kelas nilai" yang merupakan upaya untuk mengatur semakin banyak atribut non-tipe yang dapat ditunjukkan oleh ekspresi.
Namun referensi adalah tipe. Mengapa?
Dulu dijelaskan dalam tutorial C ++ bahwa deklarasi seperti const int &ri
diperkenalkan ri
sebagai memiliki tipe const int
, tetapi semantik referensi. Semantik referensi itu bukanlah sebuah tipe; itu hanyalah semacam atribut yang menunjukkan hubungan yang tidak biasa antara nama dan lokasi penyimpanan. Selain itu, fakta bahwa referensi bukanlah tipe digunakan untuk merasionalisasi mengapa Anda tidak dapat membuat tipe berdasarkan referensi, meskipun sintaks konstruksi tipe mengizinkannya. Misalnya, array atau pointer ke referensi tidak dimungkinkan: const int &ari[5]
dan const int &*pri
.
Namun sebenarnya referensi adalah tipe dan decltype(ri)
mengambil beberapa node tipe referensi yang tidak memenuhi syarat. Anda harus turun melewati node ini di pohon tipe untuk mendapatkan tipe yang mendasarinya remove_reference
.
Saat Anda menggunakan ri
, referensi tersebut secara transparan diselesaikan, sehingga ri
"terlihat dan terasa seperti i
" dan bisa disebut "alias" untuk itu. Dalam sistem tipe, ri
sebenarnya memiliki tipe yang merupakan " referensi ke const int
".
Mengapa tipe referensi?
Pertimbangkan bahwa jika referensi bukanlah tipe, maka fungsi-fungsi ini akan dianggap memiliki tipe yang sama:
void foo(int);
void foo(int &);
Itu tidak mungkin karena alasan yang cukup terbukti dengan sendirinya. Jika mereka memiliki tipe yang sama, itu berarti deklarasi mana pun akan cocok untuk kedua definisi, dan setiap (int)
fungsi harus dicurigai mengambil referensi.
Demikian pula, jika referensi bukan tipe, maka kedua deklarasi kelas ini akan menjadi setara:
class foo {
int m;
};
class foo {
int &m;
};
Ini akan benar untuk satu unit terjemahan menggunakan satu deklarasi, dan unit terjemahan lain dalam program yang sama menggunakan deklarasi lainnya.
Faktanya adalah bahwa referensi menyiratkan perbedaan dalam implementasi dan tidak mungkin memisahkannya dari tipe, karena tipe dalam C ++ berkaitan dengan implementasi entitas: "layout" -nya dalam bit sehingga untuk berbicara. Jika dua fungsi memiliki tipe yang sama, keduanya dapat dipanggil dengan konvensi pemanggilan biner yang sama: ABI-nya sama. Jika dua struct atau class memiliki tipe yang sama, tata letaknya sama seperti semantik akses ke semua anggota. Kehadiran referensi mengubah aspek tipe ini, jadi keputusan desain langsung untuk memasukkannya ke dalam sistem tipe adalah keputusan yang mudah. (Namun, perhatikan argumen balasan di sini: anggota struct / class bisa jadi static
, yang juga mengubah representasi; namun itu bukan tipe!)
Jadi, referensi dalam sistem tipe sebagai "warga kelas dua" (tidak seperti fungsi dan array dalam ISO C). Ada hal-hal tertentu yang tidak dapat kita "lakukan" dengan referensi, seperti mendeklarasikan pointer ke referensi, atau susunannya. Tapi itu tidak berarti mereka bukan tipe. Mereka bukan tipe dengan cara yang masuk akal.
Tidak semua pembatasan kelas dua ini penting. Mengingat bahwa ada struktur referensi, mungkin ada array referensi! Misalnya
int x = 0, y = 0;
int &ar[2] = { x, y };
Ini tidak diterapkan di C ++, itu saja. Pointer ke referensi sama sekali tidak masuk akal, karena pointer yang diangkat dari referensi hanya menuju ke objek yang direferensikan. Kemungkinan alasan mengapa tidak ada array referensi adalah bahwa orang-orang C ++ menganggap array sebagai semacam fitur tingkat rendah yang diwarisi dari C yang rusak dalam banyak hal yang tidak dapat diperbaiki, dan mereka tidak ingin menyentuh array sebagai dasar untuk sesuatu yang baru. Keberadaan array referensi, bagaimanapun, akan membuat contoh yang jelas tentang bagaimana referensi harus menjadi tipe.
Non const
jenis -qualifiable: ditemukan dalam ISO C90, juga!
Beberapa jawaban mengisyaratkan fakta bahwa referensi tidak mengambil const
kualifikasi. Itu agak red herring, karena deklarasi const int &ri = i
tersebut bahkan tidak mencoba membuat const
referensi yang memenuhi syarat: ini adalah referensi ke tipe yang memenuhi syarat const (yang dengan sendirinya tidak const
). Sama seperti const in *ri
mendeklarasikan pointer ke sesuatu const
, tetapi pointer itu sendiri tidak const
.
Meskipun demikian, memang benar bahwa referensi tidak dapat membawa const
kualifikasi itu sendiri.
Namun, ini tidak begitu aneh. Bahkan dalam bahasa ISO C 90, tidak semua tipe bisa const
. Yaitu, array tidak bisa.
Pertama, sintaks tidak ada untuk mendeklarasikan array const: int a const [42]
salah.
Namun, apa yang coba dilakukan oleh pernyataan di atas dapat diekspresikan melalui perantara typedef
:
typedef int array_t[42];
const array_t a;
Tapi ini tidak berfungsi seperti yang terlihat. Dalam deklarasi ini, bukan a
yang const
memenuhi syarat, tetapi elemennya! Artinya, a[0]
adalah const int
, tetapi a
hanya "array int". Akibatnya, ini tidak memerlukan diagnosis:
int *p = a;
Ini melakukan:
a[0] = 1;
Sekali lagi, ini menggarisbawahi gagasan bahwa referensi dalam arti tertentu "kelas kedua" dalam sistem tipe, seperti array.
Perhatikan bagaimana analogi tersebut berlaku lebih dalam, karena array juga memiliki "perilaku konversi yang tidak terlihat", seperti referensi. Tanpa programmer harus menggunakan operator eksplisit apa pun, pengenal a
secara otomatis berubah menjadi int *
pointer, seolah-olah ekspresi &a[0]
telah digunakan. Ini analog dengan bagaimana referensi ri
, ketika kita menggunakannya sebagai ekspresi utama, secara ajaib menunjukkan objek i
yang diikat. Ini hanyalah "pembusukan" seperti "larik ke peluruhan penunjuk".
Dan sama seperti kita tidak boleh bingung dengan "array to pointer" yang membusuk menjadi pemikiran yang salah bahwa "array hanyalah pointer dalam C dan C ++", kita juga tidak boleh berpikir bahwa referensi hanyalah alias yang tidak memiliki jenisnya sendiri.
Saat decltype(ri)
menyembunyikan konversi biasa dari referensi ke objek rujukannya, ini tidak jauh berbeda dengan sizeof a
menyembunyikan konversi array-ke-pointer, dan mengoperasikan tipe array itu sendiri untuk menghitung ukurannya.
boolalpha(cout)
sangat tidak biasa. Anda bisa melakukannyastd::cout << boolalpha
.