kesalahan: inisialisasi referensi non-const jenis 'int &' tidak valid dari nilai r jenis 'int'

87

Bentuk yang salah:

int &z = 12;

Bentuk yang benar:

int y;
int &r = y;

Pertanyaan :
Mengapa kode pertama salah? Apa " arti " dari kesalahan dalam judul?

Aquarius_Girl
sumber
5
Temporaries tidak dapat terikat pada referensi non-konstan. int (12) adalah sementara dalam kasus ini.
Prasoon Saurav
@PrasoonSaurav Apa yang Anda maksud dengan 12 sementara? Kurangnya konsep di sini (di pihak saya :))
Aquarius_Girl
2
Perhatikan bahwa tidak ada alasan teknis yang ketat untuk pembatasan ini. Ini akan semudah diimplementasikan untuk memungkinkan referensi yang bisa berubah untuk sementara. Melarang itu adalah keputusan desain C ++, karena konstruksi seperti itu akan menjadi desain yang buruk dengan risiko yang jauh lebih besar untuk disalahgunakan secara tidak sengaja daripada utilitas asli. (Saya hanya sekali menemukan kebutuhan yang dibuat-buat untuk hal seperti itu.)
Kerrek SB
@KerrekSB kebutuhan paling umum untuk ref mengikat untuk menilai objek mungkin adalah(ostringstream() << "x=" << x).str()
penasaran
@curiousguy: Ya, itulah isi dari link yang saya posting.
Kerrek SB

Jawaban:

133

C ++ 03 3.10 / 1 mengatakan: "Setiap ekspresi adalah nilai l atau nilai r." Penting untuk diingat bahwa lvalueness versus rvalueness adalah properti ekspresi, bukan objek.

Lvalues ​​nama objek yang tetap ada di luar ekspresi tunggal. Sebagai contoh, obj, *ptr, ptr[index], dan ++xsemua lvalues.

Nilai R adalah nilai temporer yang menguap pada akhir ekspresi penuh tempat nilai tersebut berada ("di titik koma"). Sebagai contoh, 1729, x + y, std::string("meow"), dan x++semua rvalues.

Operator alamat mensyaratkan bahwa "operan harus menjadi nilai l". jika kita bisa mengambil alamat dari satu ekspresi, ekspresi itu adalah nilai l, jika tidak, itu adalah nilai r.

 &obj; //  valid
 &12;  //invalid
BruceAdi
sumber
2
" jika kita bisa mengambil alamat dari satu ekspresi, ekspresi itu adalah nilai l, jika tidak, itu adalah nilai r. " jika hanya C ++ sesederhana itu! (tapi nuansanya tidak terlalu relevan di sini) "Nilai adalah sementara" sementara apa? benda?
penasaran
2
@curiousguyRvalues ​​adalah sementara yang menghilang di akhir ekspresi penuh tempat mereka tinggal. Untuk hanya menjawab mengapa express int & = 12;tidak valid, standar mengatakan string literal adalah nilai l, literal lainnya adalah nilai r.
BruceAdi
@curiousguy: Ya. Rvalues ​​adalah obyek sementara , tetapi tidak semua rvalues ​​adalah obyek sementara; beberapa bahkan bukan objek.
Nawaz
" Misalnya, 1729, x + y, std :: string (" meow "), dan x ++ semuanya adalah nilai r. " Tetapi std::string("meow")membuat objek berjenis std::stringdan menghasilkan nilai r yang menunjukkan objek ini, 1729tidak memiliki efek samping dan menghasilkan nilai 1729 sebagai nilai r dari tipe int.
penasaran
1
@curiousguy: Pernyataan tersebut "Lvalues name objects that persist beyond a single expression."adalah pernyataan yang 100% benar. Sebaliknya, contoh Anda (const int &)1salah, karena Ini BUKAN objek "bernama".
Nawaz
53
int &z = 12;

Di sisi kanan, objek sementara tipe intdibuat dari literal integral 12, tetapi sementara tidak dapat terikat ke referensi non-const. Karena itu kesalahannya. Itu sama dengan:

int &z = int(12); //still same error

Mengapa sementara dibuat? Karena referensi harus merujuk ke objek dalam memori, dan agar objek ada, itu harus dibuat terlebih dahulu. Karena objek tidak bernama, itu adalah objek sementara . Tidak ada nama. Dari penjelasan ini, menjadi sangat jelas mengapa kasus kedua baik-baik saja.

Objek sementara dapat diikat ke referensi const, yang berarti, Anda dapat melakukan ini:

const int &z = 12; //ok

C ++ 11 dan Rvalue Reference:

Demi kelengkapan, saya ingin menambahkan bahwa C ++ 11 telah memperkenalkan rvalue-reference, yang dapat mengikat objek sementara. Jadi di C ++ 11, Anda bisa menulis ini:

int && z = 12; //C+11 only 

Perhatikan bahwa ada &&pengganti &. Perhatikan juga bahwa constsudah tidak dibutuhkan lagi, padahal objek yang zdiikat merupakan objek sementara yang dibuat dari integral-literal 12.

Karena C ++ 11 telah memperkenalkan rvalue-reference , untuk int&selanjutnya disebut lvalue-reference .

Nawaz
sumber
10

12adalah konstanta waktu kompilasi yang tidak dapat diubah tidak seperti data yang dirujuk int&. Yang bisa Anda lakukan adalah

const int& z = 12;
Michael Krelin - peretas
sumber
2
@curiousguy, konsistenlah, Anda telah mengatakan "Anda harus memahami bahwa ini adalah aturan C ++. Aturan tersebut ada, dan tidak memerlukan pembenaran apa pun" (belum lagi edisi pertama). Bagaimana saya mengartikan keluhan Anda tidak memberikan apa yang menurut Anda tidak dibutuhkan?
Michael Krelin - peretas
2
@ MichaelKrelin-hacker: Secara teknis tidak, Anda tidak dapat (pernah) mengikat referensi ke nilai (atau mengkompilasi konstanta waktu), standarnya cukup eksplisit mengenai apa yang sebenarnya terjadi: Jika tidak, tipe sementara "cv1 T1" dibuat dan diinisialisasi dari ekspresi penginisialisasi menggunakan aturan untuk inisialisasi salinan non-referensi (8.5). Referensi kemudian terikat ke sementara. Artinya, sintaksis diperbolehkan, tetapi semantik tidak mengikat referensi ke konstanta, melainkan mengikatnya ke sementara yang dibuat secara tersirat.
David Rodríguez - dribeas
1
@curiousguy: Aturan bahasa adalah bagian dari desain, dan lebih sering daripada tidak, ada pembenaran mengapa bahasa itu dirancang seperti itu. Dalam kasus khusus ini, seperti di C Anda tidak diizinkan untuk mengambil alamat dari suatu nilai (tidak ada), Anda juga tidak dapat mengikat referensi. Sekarang pertimbangkan sebuah fungsi void f( vector<int> const & ), yang idiomatis melewatkan vektor yang tidak akan dimodifikasi. Masalahnya sekarang adalah itu f( vector<int>(5) )tidak benar, dan pengguna harus memberikan kelebihan beban berbeda void f( vector<int> v ) { f(v); }yang sepele.
David Rodríguez - dribeas
1
... sekarang karena itu akan menyakitkan bagi pengguna bahasa, perancang memutuskan bahwa kompilator akan melakukan operasi yang setara untuk Anda, dalam panggilan f( vector<int>(5) ), kompilator membuat sementara dan kemudian mengikat referensi ke sementara itu, dan serupa jika ada konversi implisit dari 5langsung. Hal ini memungkinkan kompiler untuk menghasilkan satu tanda tangan untuk fungsi tersebut dan memungkinkan implementasi fungsi oleh pengguna tunggal. Sejak saat itu, perilaku serupa didefinisikan untuk sisa penggunaan referensi konstan untuk konsistensi.
David Rodríguez - dribeas
1
@ MichaelKrelin-hacker: referensi adalah alias ke objek, dan nilai bukan objek. Bergantung pada konteksnya, referensi bisa saja alias kompilator menghapus referensi dan hanya menggunakan pengenal untuk mengartikan apa pun yang dimaksud objek aslinya ( T const & r = *ptr;, penggunaan nanti rdalam fungsi dapat diganti *ptr, dan rtidak perlu ada saat runtime) atau mungkin harus diimplementasikan dengan menyimpan alamat objek yang alias (pertimbangkan untuk menyimpan referensi sebagai anggota objek) - yang diimplementasikan sebagai penunjuk autodereferensi.
David Rodríguez - dribeas
4

Pengikatan referensi non-const dan const mengikuti aturan yang berbeda

Ini adalah aturan bahasa C ++:

  • ekspresi yang terdiri dari angka literal ( 12) adalah "nilai r"
  • tidak diizinkan untuk membuat referensi non-const dengan rvalue: int &ri = 12;berbentuk buruk
  • diperbolehkan untuk membuat referensi const dengan rvalue: dalam kasus ini, objek tanpa nama dibuat oleh kompilator; objek ini akan tetap ada selama referensi itu sendiri ada.

Anda harus memahami bahwa ini adalah aturan C ++. Mereka memang begitu.

Sangat mudah untuk menemukan bahasa yang berbeda, katakanlah C ++ ', dengan aturan yang sedikit berbeda. Dalam C ++ ', akan diizinkan untuk membuat referensi non-const dengan rvalue. Tidak ada yang tidak konsisten atau tidak mungkin di sini.

Tetapi itu akan memungkinkan beberapa kode berisiko di mana pemrogram mungkin tidak mendapatkan apa yang dia inginkan, dan perancang C ++ dengan tepat memutuskan untuk menghindari risiko itu.

penasaran
sumber
0

Referensi adalah "petunjuk tersembunyi" (bukan nol) ke hal-hal yang dapat berubah (lvalues). Anda tidak dapat mendefinisikannya menjadi sebuah konstanta. Ini harus menjadi hal "variabel".

EDIT ::

Saya sedang memikirkan

int &x = y;

hampir setara dengan

int* __px = &y;
#define x (*__px)

where __pxis a fresh name, and the #define xworks only inside the block of the declaration of xreference.

Basile Starynkevitch
sumber
1
Kenapa bisa, jika referensinya const:)
Michael Krelin - hacker
Tapi contoh posternya bukanlahconst
Basile Starynkevitch
Ya, maksud saya kata-kata Anda "referensi adalah petunjuk untuk hal-hal yang dapat berubah" - ini tentang referensi non-biaya.
Michael Krelin - peretas
" Referensi adalah" petunjuk tersembunyi " " salah " untuk hal-hal yang dapat mengubah hal-hal " salah " yang dapat berubah (lvalues). " Salah
penasaran
@ Loki: bisakah kamu menjelaskan lebih lanjut?
Basile Starynkevitch