Mengapa tidak diizinkan untuk mendapatkan referensi non-const ke objek sementara, yang fungsinya getx()
kembali? Jelas, ini dilarang oleh Standar C ++ tetapi saya tertarik pada tujuan pembatasan tersebut, bukan referensi ke standar.
struct X
{
X& ref() { return *this; }
};
X getx() { return X();}
void g(X & x) {}
int f()
{
const X& x = getx(); // OK
X& x = getx(); // error
X& x = getx().ref(); // OK
g(getx()); //error
g(getx().ref()); //OK
return 0;
}
- Jelas bahwa masa hidup objek tidak dapat menjadi penyebabnya, karena referensi konstan ke objek adalah tidak dilarang oleh Standar C ++.
- Jelas bahwa objek sementara tidak konstan dalam sampel di atas, karena panggilan ke fungsi yang tidak konstan diizinkan. Misalnya,
ref()
bisa memodifikasi objek sementara. - Sebagai tambahan,
ref()
memungkinkan Anda untuk menipu kompilator dan mendapatkan tautan ke objek sementara ini dan itu memecahkan masalah kami.
Sebagai tambahan:
Mereka mengatakan "menetapkan objek sementara ke referensi const memperpanjang umur objek ini" dan "Tidak ada yang dikatakan tentang referensi non-const". Pertanyaan tambahan saya . Apakah tugas berikut memperpanjang masa objek sementara?
X& x = getx().ref(); // OK
Jawaban:
Dari artikel blog Visual C ++ ini tentang referensi nilai :
Pada dasarnya, Anda tidak boleh mencoba mengubah temporari karena alasan mereka temporer dan akan mati kapan saja sekarang. Alasan Anda diizinkan memanggil metode non-const adalah karena, Anda boleh melakukan beberapa hal "bodoh" selama Anda tahu apa yang Anda lakukan dan Anda eksplisit tentang hal itu (seperti, menggunakan reinterpret_cast). Tetapi jika Anda mengikat sementara ke referensi non-const, Anda bisa terus menyebarkannya "selamanya" hanya untuk menghilangkan manipulasi objek, karena di suatu tempat di mana Anda benar-benar lupa ini adalah sementara.
Jika saya jadi Anda, saya akan memikirkan kembali desain fungsi saya. Mengapa g () menerima referensi, apakah itu memodifikasi parameter? Jika tidak, buatlah itu sebagai referensi, jika ya, mengapa Anda mencoba memberikan sementara untuk itu, tidakkah Anda peduli itu hanya sementara yang Anda modifikasi? Kenapa getx () kembali sementara? Jika Anda berbagi dengan kami skenario nyata Anda dan apa yang ingin Anda capai, Anda mungkin mendapatkan beberapa saran bagus tentang bagaimana melakukannya.
Melawan bahasa dan menipu kompiler jarang memecahkan masalah - biasanya itu menciptakan masalah.
Sunting: Mengatasi pertanyaan dalam komentar: 1)
X& x = getx().ref(); // OK when will x die?
- Saya tidak tahu dan saya tidak peduli, karena inilah yang saya maksud dengan "menentang bahasa". Bahasa mengatakan "temporaries mati pada akhir pernyataan, kecuali mereka terikat dengan referensi konstan, dalam hal ini mereka mati ketika referensi keluar dari ruang lingkup". Menerapkan aturan itu, tampaknya x sudah mati di awal pernyataan berikutnya, karena itu tidak terikat dengan referensi const (kompiler tidak tahu apa yang ref () kembali). Namun ini hanya dugaan saja.2) Saya menyatakan tujuannya dengan jelas: Anda tidak diperbolehkan untuk mengubah temporaries, karena itu tidak masuk akal (mengabaikan nilai rujukan C ++ 0x). Pertanyaannya "lalu mengapa saya diizinkan memanggil anggota non-const?" adalah jawaban yang bagus, tetapi saya tidak memiliki jawaban yang lebih baik daripada jawaban yang telah saya sebutkan di atas.
3) Nah, jika saya benar tentang x dalam
X& x = getx().ref();
sekarat di akhir pernyataan, masalahnya jelas.Ngomong-ngomong, berdasarkan pertanyaan dan komentar Anda, saya rasa jawaban ekstra ini tidak akan memuaskan Anda. Berikut ini adalah upaya / ringkasan terakhir: Komite C ++ memutuskan tidak masuk akal untuk mengubah temporari, oleh karena itu, mereka melarang pengikatan pada referensi non-const. Mungkin beberapa implementasi kompiler atau masalah bersejarah juga terlibat, saya tidak tahu. Kemudian, beberapa kasus spesifik muncul, dan diputuskan bahwa melawan segala rintangan, mereka masih akan memungkinkan modifikasi langsung melalui pemanggilan metode non-const. Tapi itu pengecualian - Anda biasanya tidak diizinkan mengubah temporer. Ya, C ++ seringkali aneh.
sumber
int
dll), tetapi diperbolehkan untuk jenis yang ditetapkan pengguna:(std::string("A")+"B").append("C")
.g()
akan memodifikasi objek (yang Anda harapkan dari fungsi mengambil referensi non-const), itu akan memodifikasi objek yang akan mati, jadi tidak ada yang bisa mendapatkan nilai yang dimodifikasi. Dia mengatakan bahwa ini, kemungkinan besar, adalah kesalahan.Dalam kode Anda
getx()
mengembalikan objek sementara, yang disebut "nilai". Anda dapat menyalin nilai ke objek (alias. Variabel) atau mengikat mereka ke referensi const (yang akan memperpanjang waktu hidup mereka sampai akhir kehidupan referensi). Anda tidak dapat mengikat nilai ke referensi non-const.Ini adalah keputusan desain yang disengaja untuk mencegah pengguna memodifikasi objek secara tidak sengaja yang akan mati di akhir ekspresi:
Jika Anda ingin melakukan ini, Anda harus membuat salinan lokal atau objek terlebih dahulu atau mengikatnya ke referensi const:
Perhatikan bahwa standar C ++ berikutnya akan mencakup referensi nilai. Oleh karena itu, apa yang Anda ketahui sebagai rujukan disebut "referensi nilai". Anda akan diizinkan untuk mengikat nilai ke nilai referensi dan Anda dapat membebani fungsi pada "nilai-nilai":
Gagasan di balik referensi nilai adalah bahwa, karena benda-benda ini akan mati, Anda dapat mengambil keuntungan dari pengetahuan itu dan mengimplementasikan apa yang disebut "pindahkan semantik", semacam optimasi tertentu:
sumber
g(getx())
tidak bekerja karena tanda tangan adalahg(X& x)
danget(x)
mengembalikan sebuah objek sementara, jadi kami tidak bisa mengikat objek sementara ( nilai p ) untuk referensi non-konstan, yang benar? Dan dalam potongan kode pertama Anda, saya pikir itu akan menjadiconst X& x2 = getx();
bukanconst X& x1 = getx();
..:-/
Ya, alasan Anda benar, meskipun sedikit terbelakang: Kami tidak dapat mengikat sementara keconst
referensi non- (nilai), dan oleh karena itu sementara dikembalikan olehgetx()
(dan tidakget(x)
) tidak dapat terikat pada referensi nilai yang menjadi alasan argumen tersebutg()
.getx()
(dan tidakget(x)
) ?getx()
, dan bukanget(x)
(seperti yang Anda tulis).Apa yang Anda tunjukkan adalah bahwa rantai operator diperbolehkan.
Ungkapannya adalah 'getx (). Ref ();' dan ini dieksekusi sampai selesai sebelum penugasan ke 'x'.
Perhatikan bahwa getx () tidak mengembalikan referensi tetapi objek yang sepenuhnya terbentuk ke dalam konteks lokal. Objek bersifat sementara tetapi bukan const, sehingga memungkinkan Anda memanggil metode lain untuk menghitung nilai atau memiliki efek samping lainnya terjadi.
Lihatlah akhir dari jawaban ini untuk contoh yang lebih baik dari ini
Anda tidak dapat mengikat sementara ke referensi karena hal itu akan menghasilkan referensi ke objek yang akan dihancurkan di akhir ungkapan sehingga meninggalkan Anda dengan referensi menggantung (yang tidak rapi dan standar tidak suka berantakan).
Nilai yang dikembalikan oleh ref () adalah referensi yang valid tetapi metode ini tidak memperhatikan umur objek yang dikembalikan (karena tidak dapat memiliki informasi dalam konteksnya). Anda pada dasarnya baru saja melakukan yang setara dengan:
Alasan tidak apa-apa untuk melakukan ini dengan referensi const ke objek sementara adalah bahwa standar memperpanjang umur sementara untuk umur referensi sehingga umur objek sementara diperpanjang melebihi akhir pernyataan.
Jadi satu-satunya pertanyaan yang tersisa adalah mengapa standar tidak ingin mengizinkan referensi ke temporaries untuk memperpanjang umur objek melampaui akhir pernyataan?
Saya percaya itu karena hal itu akan membuat kompiler sangat sulit untuk mendapatkan yang benar untuk objek sementara. Itu dilakukan untuk referensi const ke temporaries karena ini memiliki penggunaan terbatas dan dengan demikian memaksa Anda untuk membuat salinan objek untuk melakukan sesuatu yang bermanfaat tetapi memang menyediakan beberapa fungsi terbatas.
Pikirkan situasi ini:
Memperpanjang umur objek sementara ini akan sangat membingungkan.
Sedangkan yang berikut ini:
Akan memberi Anda kode yang intuitif untuk digunakan dan dipahami.
Jika Anda ingin mengubah nilai, Anda harus mengembalikan nilai ke variabel. Jika Anda mencoba untuk menghindari biaya menyalin kembali obejct dari fungsi (karena tampaknya objek tersebut adalah salinan yang dibangun kembali (secara teknis itu)). Maka jangan ganggu kompilernya sangat bagus di 'Return Value Optimization'
sumber
const_cast<x&>(getX());
tidak masuk akalMengapa dibahas dalam C ++ FAQ ( boldfacing mine):
sumber
x * 0
memberix
? Apa? Apa??incr(0);
sedemikian rupa. Jelas, jika ini diizinkan, itu akan ditafsirkan sebagai menciptakan integer sementara dan meneruskannya keincr()
Masalah utamanya adalah itu
adalah kesalahan logis:
g
memodifikasi hasilgetx()
tetapi Anda tidak memiliki kesempatan untuk memeriksa objek yang dimodifikasi. Jikag
tidak perlu mengubah parameternya maka itu tidak akan memerlukan referensi nilai, itu bisa mengambil parameter dengan nilai atau dengan referensi const.valid karena Anda terkadang perlu menggunakan kembali hasil ekspresi, dan cukup jelas bahwa Anda berurusan dengan objek sementara.
Namun itu tidak mungkin dilakukan
valid tanpa membuat
g(getx())
valid, itulah yang para desainer bahasa coba hindari sejak awal.valid karena metode hanya tahu tentang kekonstanan
this
, mereka tidak tahu apakah mereka dipanggil berdasarkan nilai atau nilai.Seperti biasa di C ++, Anda memiliki solusi untuk aturan ini tetapi Anda harus memberi sinyal kepada kompiler bahwa Anda tahu apa yang Anda lakukan dengan menjadi eksplisit:
sumber
Sepertinya pertanyaan awal mengapa ini tidak diizinkan telah dijawab dengan jelas: "karena kemungkinan besar kesalahan".
FWIW, saya pikir saya akan menunjukkan bagaimana itu bisa dilakukan, meskipun saya pikir itu bukan teknik yang baik.
Alasan saya kadang-kadang ingin meneruskan sementara ke metode mengambil referensi non-const adalah untuk sengaja membuang nilai yang dikembalikan oleh-referensi bahwa metode pemanggilan tidak peduli. Sesuatu seperti ini:
Seperti yang dijelaskan dalam jawaban sebelumnya, itu tidak dikompilasi. Tetapi ini mengkompilasi dan bekerja dengan benar (dengan kompiler saya):
Ini hanya menunjukkan bahwa Anda dapat menggunakan casting untuk berbohong kepada kompiler. Jelas, akan jauh lebih bersih untuk mendeklarasikan dan meneruskan variabel otomatis yang tidak digunakan:
Teknik ini memang memperkenalkan variabel lokal yang tidak dibutuhkan ke dalam ruang lingkup metode. Jika karena alasan tertentu Anda ingin mencegahnya agar tidak digunakan nanti dalam metode, misalnya, untuk menghindari kebingungan atau kesalahan, Anda bisa menyembunyikannya di blok lokal:
- Chris
sumber
Mengapa kamu menginginkannya
X& x = getx();
? Cukup gunakanX x = getx();
dan mengandalkan RVO.sumber
g(getx())
daripadag(getx().ref())
g
akan mengubah sesuatu yang tidak bisa Anda dapatkan lagi.Solusi jahat melibatkan kata kunci 'bisa berubah'. Sebenarnya menjadi jahat dibiarkan sebagai latihan bagi pembaca. Atau lihat di sini: http://www.ddj.com/cpp/184403758
sumber
Pertanyaan yang sangat bagus, dan inilah usaha saya untuk jawaban yang lebih ringkas (karena banyak info bermanfaat ada di komentar dan sulit untuk digali dalam keributan.)
Referensi apa pun yang terikat langsung dengan sementara akan memperpanjang umurnya [12.2.5]. Di sisi lain, referensi yang diinisialisasi dengan referensi lain tidak akan (bahkan jika itu pada akhirnya sama sementara). Itu masuk akal (kompiler tidak tahu referensi apa yang akhirnya merujuk).
Tetapi seluruh gagasan ini sangat membingungkan. Misalnya
const X &x = X();
akan membuat sementara berlangsung selamax
referensi, tetapiconst X &x = X().ref();
TIDAK akan (siapa yang tahu apa yangref()
sebenarnya dikembalikan). Dalam kasus terakhir, destructor untukX
dipanggil pada akhir baris ini. (Ini dapat diamati dengan destruktor non-sepele.)Jadi tampaknya secara umum membingungkan dan berbahaya (mengapa menyulitkan aturan tentang masa hidup objek?), Tetapi mungkin ada kebutuhan setidaknya untuk referensi const, sehingga standar mengatur perilaku ini untuk mereka.
Semua temporari bertahan sampai akhir ekspresi penuh. Untuk memanfaatkannya, Anda perlu trik seperti yang Anda miliki
ref()
. Itu legal. Kelihatannya tidak ada alasan yang baik bagi lingkaran ekstra untuk melompati, kecuali untuk mengingatkan programmer bahwa sesuatu yang tidak biasa sedang terjadi (yaitu, parameter referensi yang modifikasinya akan cepat hilang).sumber
"Jelas bahwa objek sementara tidak konstan dalam sampel di atas, karena panggilan ke fungsi yang tidak konstan diizinkan. Misalnya, ref () dapat memodifikasi objek sementara."
Dalam contoh Anda getX () tidak mengembalikan sebuah konstanta X sehingga Anda dapat memanggil ref () dengan cara yang sama seperti Anda bisa memanggil X (). Ref (). Anda mengembalikan ref non const dan dengan demikian dapat memanggil metode non const, yang tidak bisa Anda lakukan adalah menetapkan ref ke referensi non const.
Bersamaan dengan komentar SadSidos ini membuat tiga poin Anda salah.
sumber
Saya memiliki skenario yang ingin saya bagikan di mana saya berharap dapat melakukan apa yang diminta Alexey. Dalam plugin Maya C ++, saya harus melakukan shenanigan berikut untuk mendapatkan nilai ke atribut node:
Ini membosankan untuk menulis, jadi saya menulis fungsi pembantu berikut:
Dan sekarang saya dapat menulis kode dari awal posting sebagai:
Masalahnya adalah ia tidak dikompilasi di luar Visual C ++, karena ia mencoba untuk mengikat variabel sementara yang dikembalikan dari | operator ke referensi MPlug dari << operator. Saya ingin itu menjadi referensi karena kode ini disebut berkali-kali dan saya lebih suka tidak disalin MPlug begitu banyak. Saya hanya perlu objek sementara untuk hidup sampai akhir fungsi kedua.
Nah, ini skenario saya. Hanya berpikir saya akan menunjukkan contoh di mana orang ingin melakukan apa yang dijelaskan Alexey. Saya menyambut semua kritik dan saran!
Terima kasih.
sumber