Dalam C ++ 03, ekspresi bisa berupa nilai p atau lvalue .
Di C ++ 11, sebuah ekspresi bisa menjadi:
- nilai
- lvalue
- nilai x
- nilai glv
- nilai awal
Dua kategori telah menjadi lima kategori.
- Apa saja kategori ekspresi yang baru ini?
- Bagaimana kategori baru ini berhubungan dengan kategori nilai dan nilai yang ada?
- Apakah kategori rvalue dan lvalue dalam C ++ 0x sama dengan yang ada di C ++ 03?
- Mengapa kategori baru ini dibutuhkan? Apakah para dewa WG21 hanya mencoba membingungkan kita hanya manusia biasa?
c++
expression
c++-faq
c++11
James McNellis
sumber
sumber
string("hello") = string("world")
.Jawaban:
Saya kira dokumen ini mungkin berfungsi sebagai pengantar yang tidak terlalu singkat: n3055
Seluruh pembantaian dimulai dengan gerakan semantik. Begitu kita memiliki ekspresi yang dapat dipindahkan dan tidak disalin, tiba-tiba mudah untuk memahami aturan yang menuntut perbedaan antara ekspresi yang dapat dipindahkan, dan ke arah mana.
Dari apa yang saya kira berdasarkan draft, perbedaan nilai r / l tetap sama, hanya dalam konteks memindahkan barang menjadi berantakan.
Apakah mereka dibutuhkan? Mungkin tidak jika kita ingin kehilangan fitur baru. Tetapi untuk memungkinkan optimalisasi yang lebih baik, kita mungkin harus merangkul mereka.
Mengutip n3055 :
E
merupakan ekspresi dari tipe pointer, maka*E
adalah ekspresi lvalue yang merujuk pada objek atau fungsi keE
titik mana . Sebagai contoh lain, hasil memanggil fungsi yang tipe kembaliannya merupakan referensi nilai adalah nilai.]Dokumen yang dimaksud adalah referensi yang bagus untuk pertanyaan ini, karena menunjukkan perubahan tepat dalam standar yang telah terjadi sebagai hasil dari pengenalan nomenklatur baru.
sumber
The FCD (n3092) memiliki deskripsi yang sangat baik:
Saya sarankan Anda membaca seluruh bagian 3.10 Nilai dan nilai meskipun.
Lagi:
Semantik nilai-nilai telah berkembang terutama dengan diperkenalkannya semantik bergerak.
Sehingga pemindahan konstruksi / penugasan dapat didefinisikan dan didukung.
sumber
glvalue
sebagailvalue
danlvalue
sebagaiplvalue
, agar konsisten?Saya akan mulai dengan pertanyaan terakhir Anda:
Standar C ++ berisi banyak aturan yang berhubungan dengan kategori nilai ekspresi. Beberapa aturan membuat perbedaan antara nilai dan nilai. Misalnya, ketika datang ke resolusi kelebihan beban. Aturan lain membuat perbedaan antara glvalue dan prvalue. Misalnya, Anda dapat memiliki glvalue dengan tipe yang tidak lengkap atau abstrak tetapi tidak ada nilai dengan tipe yang tidak lengkap atau abstrak. Sebelum kita memiliki terminologi ini, aturan yang benar-benar perlu membedakan antara glvalue / prvalue disebut lvalue / rvalue dan keduanya salah secara tidak sengaja atau mengandung banyak penjelasan dan pengecualian pada aturan a la "... kecuali jika nilainya disebabkan karena tidak disebutkan namanya. referensi nilai ... ". Jadi, sepertinya ide yang bagus untuk hanya memberikan konsep nilai-nilai dan nilai-nilai mereka sendiri.
Kami masih memiliki istilah lvalue dan rvalue yang kompatibel dengan C ++ 98. Kami baru saja membagi nilai-nilai menjadi dua subkelompok, nilai-nilai x dan nilai-nilai, dan kami menyebut nilai-nilai dan nilai-nilai x sebagai nilai-rendah. Xvalues adalah jenis baru dari kategori nilai untuk referensi nilai tak bernama. Setiap ekspresi adalah salah satu dari ketiganya: lvalue, xvalue, prvalue. Diagram Venn akan terlihat seperti ini:
Contoh dengan fungsi:
Tetapi juga jangan lupa bahwa referensi nilai yang dinamai adalah nilai:
sumber
Saya tidak merasa bahwa jawaban yang lain (baik meskipun banyak dari mereka) benar-benar menangkap jawaban untuk pertanyaan khusus ini. Ya, kategori ini dan semacamnya ada untuk memungkinkan semantik bergerak, tetapi kompleksitas ada karena satu alasan. Ini adalah aturan yang tidak berlaku untuk memindahkan barang di C ++ 11:
Anda hanya akan bergerak jika tidak aman untuk melakukannya.
Itulah sebabnya kategori-kategori ini ada: untuk dapat berbicara tentang nilai-nilai yang aman untuk dipindahkan darinya, dan untuk berbicara tentang nilai-nilai yang tidak aman.
Dalam versi referensi r-value paling awal, perpindahan terjadi dengan mudah. Terlalu mudah Cukup mudah sehingga ada banyak potensi untuk memindahkan barang secara implisit ketika pengguna tidak benar-benar bermaksud.
Berikut adalah keadaan di mana aman untuk memindahkan sesuatu:
Jika kamu melakukan ini:
Apa fungsinya? Dalam versi yang lebih lama dari spec, sebelum 5 nilai masuk, ini akan memancing langkah. Tentu saja. Anda meneruskan referensi nilai ke konstruktor, dan karenanya mengikat ke konstruktor yang mengambil referensi nilai. Itu sudah jelas.
Hanya ada satu masalah dengan ini; Anda tidak meminta untuk memindahkannya. Oh, Anda mungkin mengatakan bahwa itu
&&
seharusnya petunjuk, tapi itu tidak mengubah fakta bahwa itu melanggar aturan.val
bukan sementara karena temporer tidak memiliki nama. Anda mungkin telah memperpanjang masa pakai sementara, tetapi itu berarti itu tidak sementara ; itu seperti variabel stack lainnya.Jika ini bukan sementara, dan Anda tidak meminta untuk memindahkannya, maka pindah itu salah.
Solusi yang jelas adalah membuat
val
nilai. Ini berarti bahwa Anda tidak dapat bergerak darinya. Baiklah; itu dinamai, jadi itu adalah nilai.Setelah Anda melakukan itu, Anda tidak bisa lagi mengatakan itu
SomeType&&
berarti hal yang sama di mana pun. Anda sekarang telah membuat perbedaan antara referensi nilai yang disebutkan dan referensi nilai yang tidak disebutkan namanya. Yah, referensi nilai yang dinamai adalah nilai; itulah solusi kami di atas. Jadi apa yang kita sebut referensi nilai tak bernama (nilai pengembalian dariFunc
atas)?Ini bukan lvalue, karena Anda tidak bisa bergerak dari lvalue. Dan kita harus bisa bergerak dengan mengembalikan a
&&
; bagaimana lagi yang bisa Anda katakan secara eksplisit untuk memindahkan sesuatu? Lagipula itulah yangstd::move
kembali. Itu bukan nilai (gaya lama), karena bisa di sisi kiri persamaan (hal-hal sebenarnya sedikit lebih rumit, lihat pertanyaan ini dan komentar di bawah). Itu bukan nilai atau nilai; itu hal baru.Apa yang kami miliki adalah nilai yang dapat Anda perlakukan sebagai lvalue, kecuali bahwa nilai itu secara implisit dapat dipindahkan. Kami menyebutnya nilai x.
Perhatikan bahwa xvalues adalah apa yang membuat kami memperoleh dua kategori nilai lainnya:
Prvalue benar-benar hanya nama baru untuk tipe nilai sebelumnya, yaitu nilai yang bukan nilai x.
Glvalues adalah gabungan dari nilai x dan nilai dalam satu kelompok, karena mereka memiliki banyak kesamaan properti.
Jadi sungguh, itu semua bermuara pada nilai-nilai x dan kebutuhan untuk membatasi pergerakan hanya ke tempat-tempat tertentu saja. Tempat-tempat itu ditentukan oleh kategori nilai; prvalues adalah gerakan implisit, dan xvalues adalah gerakan eksplisit (
std::move
mengembalikan nilai x).sumber
&&
.X foo(); foo() = X;
... Untuk alasan mendasar ini, saya tidak bisa mengikuti jawaban yang sangat baik sampai akhir, karena Anda benar-benar hanya membuat perbedaan antara xvalue baru, dan prvalue gaya lama, berdasarkan pada fakta bahwa itu bisa di lhs.X
menjadi kelas;X foo();
menjadi deklarasi fungsi, danfoo() = X();
menjadi garis kode. (Saya meninggalkan set kedua tanda kurung difoo() = X();
dalam komentar saya di atas.) Untuk pertanyaan yang baru saja saya posting dengan penggunaan ini disorot, lihat stackoverflow.com/questions/15482508/…IMHO, penjelasan terbaik tentang artinya memberi kami Stroustrup + memperhitungkan contoh akun Dániel Sándor dan Mohan :
Stroustrup:
sumber
lvalue
s, semua literal lainnya adalahprvalue
s. Sebenarnya Anda bisa membuat argumen untuk mengatakan non-string literal tidak bisa digerakkan, tapi itu bukan standar yang ditulis.PENGANTAR
ISOC ++ 11 (secara resmi ISO / IEC 14882: 2011) adalah versi terbaru dari standar bahasa pemrograman C ++. Ini berisi beberapa fitur baru, dan konsep, misalnya:
Jika kami ingin memahami konsep kategori nilai ekspresi baru, kami harus mengetahui bahwa ada referensi nilai dan nilai. Lebih baik untuk mengetahui nilai dapat diteruskan ke referensi nilai non-const.
Kita dapat memperoleh beberapa intuisi dari konsep kategori nilai jika kita mengutip sub-bagian berjudul Lvalues dan rvalues dari draft kerja N3337 (draft paling mirip dengan standar ISOC ++ 11 yang diterbitkan).
Tetapi saya tidak begitu yakin tentang ayat ini cukup untuk memahami konsep-konsep dengan jelas, karena "biasanya" tidak benar-benar umum, "mendekati akhir masa hidupnya" tidak benar-benar konkret, "melibatkan referensi nilai" tidak benar-benar jelas, dan "Contoh: Hasil memanggil fungsi yang tipe kembaliannya merupakan referensi nilai adalah nilai x." terdengar seperti ular menggigit ekornya.
KATEGORI NILAI PRIMER
Setiap ekspresi memiliki tepat satu kategori nilai primer. Kategori-kategori nilai ini adalah kategori nilai, nilai, dan kategori nilai awal.
nilai-nilai
Ekspresi E milik kategori nilai jika dan hanya jika E merujuk ke entitas yang SUDAH memiliki identitas (alamat, nama atau alias) yang membuatnya dapat diakses di luar E.
nilai x
Ekspresi E milik kategori xvalue jika dan hanya jika itu
- hasil memanggil suatu fungsi, baik secara implisit atau eksplisit, yang tipe pengembaliannya merupakan rvalue referensi ke tipe objek yang dikembalikan, atau
- pemeran untuk referensi nilai ke tipe objek, atau
- ekspresi akses anggota kelas yang menunjuk anggota data non-statis dari tipe non-referensi di mana ekspresi objek adalah xvalue, atau
- ekspresi pointer-ke-anggota di mana operan pertama adalah nilai x dan operan kedua adalah pointer ke anggota data.
Perhatikan bahwa efek dari aturan di atas adalah bahwa rujukan nilai yang dinamai objek diperlakukan sebagai lvalues dan referensi rvalue yang tidak disebutkan namanya untuk objek diperlakukan sebagai nilai x; referensi nilai untuk fungsi diperlakukan sebagai nilai apakah namanya atau tidak.
nilai-nilai
Ekspresi E termasuk dalam kategori nilai awal jika dan hanya jika E tidak termasuk dalam kategori nilai rendah atau juga untuk kategori harga rendah.
KATEGORI NILAI CAMPURAN
Ada dua kategori nilai campuran lebih lanjut yang penting. Kategori nilai ini adalah kategori rvalue dan glvalue.
nilai-nilai
Ekspresi E milik kategori nilai jika dan hanya jika E milik kategori nilai, atau ke kategori nilai awal.
Perhatikan bahwa definisi ini berarti bahwa ekspresi E milik kategori nilai jika dan hanya jika E mengacu pada entitas yang tidak memiliki identitas yang membuatnya dapat diakses di luar E YET.
nilai-nilai
Ekspresi E milik kategori glvalue jika dan hanya jika E milik kategori lvalue, atau ke kategori xvalue.
ATURAN PRAKTIS
Scott Meyer telah menerbitkan aturan praktis yang sangat berguna untuk membedakan nilai-nilai dari nilai-nilai.
sumber
struct As{void f(){this;}}
parathis
variabel prvalue a. Saya pikirthis
seharusnya menjadi nilai. Sampai standar 9.3.2 mengatakan: Di dalam fungsi anggota non-statis (9.3), kata kunci ini adalah ekspresi nilai awal.this
adalah nilai awal tetapi*this
nilai rendah"www"
tidak selalu memiliki alamat yang sama. Ini adalah nilai karena merupakan array .Kategori C ++ 03 terlalu terbatas untuk menangkap pengenalan referensi nilai dengan benar ke atribut ekspresi.
Dengan diperkenalkannya mereka, dikatakan bahwa referensi nilai yang tidak disebutkan namanya mengevaluasi ke suatu nilai, sehingga resolusi kelebihan beban akan lebih memilih ikatan referensi nilai, yang akan membuatnya memilih konstruktor bergerak daripada konstruktor salinan. Tetapi ditemukan bahwa ini menyebabkan masalah di sekitar, misalnya dengan Jenis Dinamis dan dengan kualifikasi.
Untuk menunjukkan ini, pertimbangkan
Pada konsep pra-xvalue, ini diizinkan, karena dalam C ++ 03, nilai jenis non-kelas tidak pernah memenuhi syarat cv. Tapi ini dimaksudkan yang
const
berlaku dalam kasus referensi-nilai, karena di sini kita lakukan merujuk ke objek (= memori!), Dan menjatuhkan const dari rvalues non-kelas terutama untuk alasan bahwa tidak ada objek di sekitar.Masalah untuk tipe dinamis bersifat serupa. Di C ++ 03, nilai dari tipe kelas memiliki tipe dinamis yang dikenal - itu adalah tipe statis dari ekspresi itu. Karena untuk mendapatkannya dengan cara lain, Anda perlu referensi atau referensi, yang mengevaluasi ke nilai. Itu tidak benar dengan referensi nilai yang tidak disebutkan namanya, namun mereka dapat menunjukkan perilaku polimorfik. Jadi untuk menyelesaikannya,
referensi nilai tidak bernama menjadi nilai- x . Mereka dapat dikualifikasi dan berpotensi memiliki tipe dinamis yang berbeda. Mereka memang, seperti yang dimaksudkan, lebih suka referensi nilai saat overloading, dan tidak akan mengikat ke nilai referensi non-konstan.
Apa yang sebelumnya adalah nilai p (literal, benda yang diciptakan oleh gips untuk jenis non-reference) sekarang menjadi prvalue . Mereka memiliki preferensi yang sama dengan nilai x selama overloading.
Apa yang sebelumnya adalah nilai tetap nilai.
Dan dua pengelompokan dilakukan untuk menangkap yang dapat dikualifikasi dan dapat memiliki tipe dinamis yang berbeda ( glvalues ) dan yang mana kelebihan beban lebih memilih pengikatan referensi nilai-nilai ( rvalues ).
sumber
Saya telah berjuang dengan ini untuk waktu yang lama, sampai saya menemukan penjelasan cppreference.com dari kategori nilai .
Ini sebenarnya agak sederhana, tetapi saya menemukan bahwa itu sering dijelaskan dengan cara yang sulit untuk dihafal. Di sini dijelaskan dengan sangat skematis. Saya akan mengutip beberapa bagian halaman:
sumber
Nilai C ++ 03 masih merupakan nilai C ++ 11, sedangkan nilai C ++ 03 disebut nilai awal dalam C ++ 11.
sumber
Satu tambahan untuk jawaban bagus di atas, pada titik yang membingungkan saya bahkan setelah saya membaca Stroustrup dan berpikir saya memahami perbedaan nilai / nilai. Ketika kamu melihat
int&& a = 3
,sangat menggoda untuk membaca
int&&
sebagai tipe dan menyimpulkan bahwa itua
adalah nilai. Ini bukan:a
memiliki nama dan ipso facto merupakan nilai tambah. Jangan menganggapnya&&
sebagai bagian dari tipea
; itu hanya sesuatu yang memberi tahu Anda apaa
yang diizinkan untuk diikat.Ini penting terutama untuk
T&&
mengetikkan argumen dalam konstruktor. Jika kamu menulisFoo::Foo(T&& _t) : t{_t} {}
Anda akan menyalin
_t
ke dalamt
. Kamu butuhFoo::Foo(T&& _t) : t{std::move(_t)} {}
jika ingin pindah. Apakah itu kompiler saya memperingatkan saya ketika saya meninggalkanmove
!sumber
a
yang diizinkan untuk diikat": Tentu, tetapi pada baris 2 & 3 variabel Anda adalah c & b, dan itu bukan yang mengikat, dan jenisnyaa
tidak relevan di sini, bukan? Garis akan sama jikaa
dideklarasikanint a
. Perbedaan utama sebenarnya di sini adalah bahwa pada baris 1 a tidak harusconst
terikat ke 3.Karena jawaban sebelumnya membahas teori di balik kategori nilai, ada satu hal lain yang ingin saya tambahkan: Anda dapat benar-benar bermain dengannya dan mengujinya.
Untuk beberapa eksperimen langsung dengan kategori nilai, Anda dapat menggunakan penentu jenis deklarasi . Perilakunya secara eksplisit membedakan antara tiga kategori nilai primer (xvalue, lvalue, dan prvalue).
Menggunakan preprocessor menyelamatkan kita dari pengetikan ...
Kategori utama:
Kategori campuran:
Sekarang kita dapat mereproduksi (hampir) semua contoh dari cppreference pada kategori nilai .
Berikut adalah beberapa contoh dengan C ++ 17 (untuk terse static_assert):
Kategori campuran agak membosankan setelah Anda menemukan kategori utama.
Untuk beberapa contoh lainnya (dan percobaan), lihat tautan berikut tentang kompiler explorer . Namun, jangan repot-repot membaca majelis. Saya menambahkan banyak kompiler hanya untuk memastikan itu bekerja di semua kompiler umum.
sumber
#define IS_GLVALUE(X) IS_LVALUE(X) || IS_XVALUE(X)
seharusnya benar-benar#define IS_GLVALUE(X) (IS_LVALUE(X) || IS_XVALUE(X))
melihat apa yang terjadi jika kalian&&
berduaIS_GLVALUE
.