Kita tahu bahwa "variabel const" menunjukkan bahwa setelah ditetapkan, Anda tidak dapat mengubah variabel, seperti ini:
int const i = 1;
i = 2;
Program di atas akan gagal untuk dikompilasi; gcc meminta dengan kesalahan:
assignment of read-only variable 'i'
Tidak masalah, saya bisa memahaminya, tetapi contoh berikut di luar pemahaman saya:
#include<iostream>
using namespace std;
int main()
{
boolalpha(cout);
int const i = 1;
cout << is_const<decltype(i)>::value << endl;
int const &ri = i;
cout << is_const<decltype(ri)>::value << endl;
return 0;
}
Ini menghasilkan
true
false
Aneh. Kita tahu bahwa begitu referensi terikat ke nama / variabel, kita tidak bisa mengubah pengikatan ini, kita mengubah objek terikatnya. Jadi saya kira tipe ri
harus sama dengan i
: kapan i
an int const
, mengapa ri
tidak const
?
boolalpha(cout)
sangat tidak biasa. Anda bisa melakukannyastd::cout << boolalpha
.ri
menjadi "alias" yang tidak bisa dibedakani
.i
juga merupakan rujukan tetapi karena alasan historis Anda tidak menyatakannya secara eksplisit. Dengan demikiani
adalah referensi yang mengacu pada suatu penyimpanan danri
merupakan referensi yang mengacu pada penyimpanan yang sama. Tetapi tidak ada perbedaan di alam antarai
danri
. Karena Anda tidak dapat mengubah pengikatan referensi, Anda tidak perlu mengkualifikasinya sebagaiconst
. Dan izinkan saya mengklaim bahwa komentar @Kaz jauh lebih baik daripada jawaban yang divalidasi (jangan pernah menjelaskan referensi menggunakan pointer, ref adalah nama, ptr adalah variabel).is_const
untuk kembalitrue
dalam kasus ini juga. Menurut pendapat saya, ini adalah contoh yang baik mengapaconst
secara fundamental terbelakang; atribut "bisa berubah" (a la Rust'smut
) sepertinya akan lebih konsisten.is_const<int const &>::value
salah?" atau serupa; Saya berjuang untuk melihat arti dari pertanyaan selain bertanya tentang perilaku sifat tipeJawaban:
Ini mungkin tampak kontra-intuitif tetapi saya pikir cara untuk memahami ini adalah dengan menyadari bahwa, dalam hal tertentu, referensi diperlakukan secara sintaksis seperti petunjuk .
Ini tampaknya logis untuk sebuah penunjuk :
int main() { boolalpha(cout); int const i = 1; cout << is_const<decltype(i)>::value << endl; int const* ri = &i; cout << is_const<decltype(ri)>::value << endl; }
Keluaran:
true false
Ini logis karena kita tahu ini bukan objek penunjuk yang const (dapat dibuat untuk menunjuk ke tempat lain) itu adalah objek yang sedang diarahkan.
Jadi kita dengan benar melihat keteguhan dari pointer itu sendiri yang dikembalikan sebagai
false
.Jika kita ingin membuat pointernya sendiri,
const
kita harus mengatakan:int main() { boolalpha(cout); int const i = 1; cout << is_const<decltype(i)>::value << endl; int const* const ri = &i; cout << is_const<decltype(ri)>::value << endl; }
Keluaran:
true true
Jadi saya pikir kita melihat analogi sintaksis dengan referensi .
Namun referensi secara semantik berbeda dengan pointer terutama dalam satu hal penting, kami tidak diizinkan untuk me - rebind referensi ke objek lain setelah terikat.
Jadi meskipun referensi memiliki sintaks yang sama dengan pointer , aturannya berbeda sehingga bahasa mencegah kita mendeklarasikan referensi itu sendiri
const
seperti ini:int main() { boolalpha(cout); int const i = 1; cout << is_const<decltype(i)>::value << endl; int const& const ri = i; // COMPILE TIME ERROR! cout << is_const<decltype(ri)>::value << endl; }
Saya berasumsi kita tidak diperbolehkan melakukan ini karena tampaknya tidak diperlukan ketika aturan bahasa mencegah referensi dari rebound dengan cara yang sama seperti pointer (jika tidak dideklarasikan
const
).Jadi untuk menjawab pertanyaan:
Dalam contoh Anda, sintaksis membuat sesuatu dirujuk dengan
const
cara yang sama seperti jika Anda mendeklarasikan sebuah pointer .Benar atau salah kita tidak diperbolehkan membuat referensi itu sendiri
const
tetapi jika kita ada maka akan terlihat seperti ini:int const& const ri = i; // not allowed
Mengapa
decltype()
tidak ditransfer ke objek referensi terikat?Saya kira ini untuk persamaan semantik dengan pointer dan mungkin juga fungsi dari
decltype()
(tipe yang dideklarasikan) adalah untuk melihat kembali apa yang dideklarasikan sebelum pengikatan terjadi.sumber
decltype
juga, dan ternyata tidak.const
, karena itustd::is_const
harus kembalifalse
. Mereka malah bisa menggunakan kata-kata yang berarti harus kembalitrue
, tapi ternyata tidak. Itu dia! Semua hal tentang pointer, "Saya berasumsi", "Saya kira", dll tidak memberikan penjelasan yang nyata.decltype
ini tidak dihitung karena bukan operasi runtime, jadi ide "saat runtime, referensi berperilaku seperti pointer", apakah benar atau tidak, tidak benar-benar berlaku.std::is_const
memeriksa apakah tipe tersebut memenuhi syarat const atau tidak.Tapi referensi tidak bisa memenuhi syarat konstitusi. Referensi [dcl.ref] / 1
Jadi
is_const<decltype(ri)>::value
akan kembalifalse
karenari
(referensi) bukan tipe yang memenuhi syarat const. Seperti yang Anda katakan, kami tidak dapat me-rebind referensi setelah inisialisasi, yang berarti referensi selalu "const", di sisi lain, referensi yang memenuhi syarat const atau referensi tanpa kualifikasi const mungkin tidak masuk akal sebenarnya.sumber
is_const
kembalitrue
? Jawaban itu mencoba menarik analogi tentang bagaimana pointer secara opsional dapat dipasang ulang, sedangkan referensi tidak - dan dengan demikian, mengarah pada kontradiksi diri untuk alasan yang sama. Saya tidak yakin ada penjelasan yang sebenarnya, selain keputusan yang agak sewenang-wenang oleh mereka yang menulis Standar, dan terkadang itulah yang terbaik yang bisa kita harapkan. Karena itu jawaban ini.decltype
ini bukan fungsi dan karena itu bekerja langsung pada referensi itu sendiri bukan pada disebut-ke objek. (Ini mungkin lebih relevan dengan jawaban "referensi pada dasarnya adalah penunjuk", tetapi saya masih berpikir itu adalah bagian dari apa yang membuat contoh ini membingungkan dan oleh karena itu perlu disebutkan di sini.)decltype(name)
bertindak berbeda dari seorang jenderaldecltype(expr)
. Jadi misalnya,decltype(i)
adalah tipe yang dideklarasikani
isconst int
, sementaradecltype((i))
akanint const &
.Anda perlu menggunakan
std::remove_reference
untuk mendapatkan nilai yang Anda cari.std::cout << std::is_const<std::remove_reference<decltype(ri)>::type>::value << std::endl;
Untuk informasi lebih lanjut, lihat posting ini .
sumber
Mengapa makro tidak
const
? Fungsi? Literals? Nama-nama jenisnya?const
hal-hal hanyalah sebagian dari hal-hal yang tidak dapat diubah.Karena tipe referensi hanya itu - tipe - mungkin masuk akal untuk meminta
const
-qualifier pada mereka semua untuk simetri dengan tipe lain (terutama dengan tipe pointer), tapi ini akan menjadi sangat membosankan dengan sangat cepat.Jika C ++ memiliki objek yang tidak dapat diubah secara default, memerlukan
mutable
kata kunci pada apa pun yang tidak Anda inginkanconst
, maka ini akan mudah: jangan izinkan pemrogram menambahkanmutable
ke jenis referensi.Karena adanya, mereka tidak dapat diubah tanpa kualifikasi.
Dan, karena mereka tidak
const
-kualifikasi, mungkin akan lebih membingungkan untukis_const
tipe referensi untuk menghasilkan true.Saya menemukan ini sebagai kompromi yang masuk akal, terutama karena keabadian itu ditegakkan oleh fakta bahwa tidak ada sintaks untuk mengubah referensi.
sumber
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 manaextern
menunjukkan kelas penyimpanan statis dan adanya linkage. Jenisnya adilconst 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
diperkenalkanri
sebagai memiliki tipeconst 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]
danconst 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 mendasarinyaremove_reference
.Saat Anda menggunakan
ri
, referensi tersebut secara transparan diselesaikan, sehinggari
"terlihat dan terasa sepertii
" dan bisa disebut "alias" untuk itu. Dalam sistem tipe,ri
sebenarnya memiliki tipe yang merupakan " referensi keconst 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
// fantasy syntax int x = 0, y = 0; int &ar[2] = { x, y }; // ar[0] is now an alias for x: could be useful!
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 deklarasiconst int &ri = i
tersebut bahkan tidak mencoba membuatconst
referensi yang memenuhi syarat: ini adalah referensi ke tipe yang memenuhi syarat const (yang dengan sendirinya tidakconst
). Sama seperticonst in *ri
mendeklarasikan pointer ke sesuatuconst
, tetapi pointer itu sendiri tidakconst
.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
yangconst
memenuhi syarat, tetapi elemennya! Artinya,a[0]
adalahconst int
, tetapia
hanya "array int". Akibatnya, ini tidak memerlukan diagnosis:int *p = a; /* surprise! */
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 menjadiint *
pointer, seolah-olah ekspresi&a[0]
telah digunakan. Ini analog dengan bagaimana referensiri
, ketika kita menggunakannya sebagai ekspresi utama, secara ajaib menunjukkan objeki
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 dengansizeof a
menyembunyikan konversi array-ke-pointer, dan mengoperasikan tipe array itu sendiri untuk menghitung ukurannya.sumber
decltype
tidak melakukan resolusi transparan ini (ini bukan fungsi, jadiri
tidak "digunakan" dalam arti yang Anda gambarkan). Ini cocok di sangat baik dengan seluruh fokus Anda pada sistem jenis - mereka koneksi utama adalah bahwadecltype
adalah operasi jenis-sistem .const X & x ”berarti x alias objek X, tetapi Anda tidak dapat mengubah objek X tersebut melalui x.
Dan lihat std :: is_const .
sumber