Pertanyaan Apa rvalues, nilai, xvalues, glalue, dan prvalues?


Dalam C ++ 03, ekspresi adalah salah satu rvalue atau sebuah lvalue.

Di C ++ 11, ekspresi dapat berupa:

  1. rvalue
  2. lvalue
  3. xvalue
  4. glvalue
  5. prvalue

Dua kategori telah menjadi lima kategori.

  • Apa kategori baru dari ekspresi ini?
  • Bagaimana kategori-kategori baru ini berhubungan dengan kategori rvalue dan lvalue yang ada?
  • Apakah kategori rvalue dan lvalue di C ++ 0x sama dengan di C ++ 03?
  • Mengapa kategori-kategori baru ini dibutuhkan? Apakah itu WG21 tuhan hanya mencoba membingungkan kita manusia biasa?

1104
2017-08-30 15:02


asal


Jawaban:


Saya kira dokumen ini mungkin berfungsi sebagai pengantar yang tidak begitu singkat: n3055

Seluruh pembantaian dimulai dengan gerakan semantik. Setelah kita memiliki ekspresi yang dapat dipindahkan dan tidak disalin, tiba-tiba mudah untuk memahami peraturan 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 hal-hal menjadi berantakan.

Apakah mereka dibutuhkan? Mungkin tidak jika kita ingin kehilangan fitur-fitur baru. Tetapi untuk memungkinkan pengoptimalan yang lebih baik, kita mungkin harus merangkulnya.

Mengutip n3055:

  • Sebuah lvalue (disebut, secara historis, karena nilai dapat muncul di sisi kiri tugas ekspresi) menunjuk fungsi atau Sebuah Objek. [Contoh: Jika E adalah ekspresi jenis pointer, lalu *E adalah ekspresi nilai yang mengacu pada objek atau fungsi yang mana E poin. Sebagai contoh lain, hasil pemanggilan fungsi yang jenis pengembalian adalah referensi lnilai sebuah lvalue.] 
  • Sebuah xvalue (sebuah Nilai "eXpiring") juga mengacu pada objek, biasanya dekat ujungnya seumur hidup (agar sumber dayanya bisa dipindahkan, misalnya). Nilai x adalah hasil dari beberapa jenis tertentu ekspresi yang melibatkan rvalue referensi. [Contoh: The hasil pemanggilan fungsi yang jenis pengembalian adalah referensi rvalue sebuah xvalue.]
  • SEBUAH glvalue   (Nilai "umum") adalah sebuah lvalue atau sebuah xvalue.
  • Sebuah rvalue (disebut, secara historis, karena rvalu bisa muncul di sisi kanan ekspresi tugas) adalah nilai x, objek sementara atau subobjek daripadanya, atau nilai itu tidak terkait dengan suatu objek.
  • SEBUAH prvalue ("Murni" rvalue) adalah rvalue itu bukan xvalue. [Contoh: The hasil pemanggilan fungsi yang Jenis kembalinya bukan referensi adalah a prvalue]

Dokumen yang dimaksud adalah referensi yang bagus untuk pertanyaan ini, karena menunjukkan perubahan yang tepat dalam standar yang telah terjadi sebagai akibat dari pengenalan nomenklatur baru.


530
2017-08-30 15:09



Apa kategori baru dari ekspresi ini?

Itu FCD (n3092) memiliki deskripsi yang sangat bagus:

- Sebuah lvalue (disebut, secara historis, karena nilai-nilai dapat muncul pada   sisi kiri tugas   ekspresi) menunjuk fungsi atau   Sebuah Objek. [Contoh: Jika E adalah   ekspresi jenis pointer, lalu   * E adalah ekspresi nilai yang mengacu pada objek atau fungsi yang E   poin. Sebagai contoh lain, hasilnya   memanggil fungsi yang kembali   ketik adalah referensi lnilai adalah sebuah   lvalue. —selesai contoh]

- Nilai x (an   Nilai "eXpiring") juga mengacu pada   objek, biasanya dekat ujungnya   seumur hidup (sehingga sumber dayanya mungkin   pindah, misalnya). Nilai x adalah   hasil dari beberapa jenis ekspresi tertentu   melibatkan rujukan nilai (8.3.2). [   Contoh: Hasil panggilan a   fungsi yang jenis kembalinya adalah   rujukan referensi adalah xvalue. -akhir   contoh]

- Nilai gla ("umum"   lvalue) adalah lvalue atau nilai x.

-   Suatu rvalue (disebut, secara historis,   karena rvalues ​​dapat muncul di   sisi kanan tugas   ekspresi) adalah nilai x, sementara   objek (12.2) atau subobjek daripadanya, atau   nilai yang tidak terkait dengan   obyek.

- Prvalue (nilai "murni") adalah   sebuah rvalue yang bukan merupakan xvalue. [   Contoh: Hasil panggilan a   fungsi yang jenis kembalinya bukan   referensi adalah prvalue. Nilai dari a   literal seperti 12, 7.3e5, atau benar adalah   juga prvalue. —selesai contoh]

Setiap   ekspresi milik salah satunya   klasifikasi mendasar dalam   taksonomi ini: lvalue, xvalue, atau   prvalue. Properti ini dari sebuah   Ekspresi disebut nilainya   kategori. [Catatan: Diskusi tentang   masing-masing operator bawaan dalam Klausul 5   menunjukkan kategori nilai itu   hasil dan kategori nilai dari   operan yang diharapkannya. Misalnya,   operator penugasan yang ada di dalamnya mengharapkan   bahwa operan kiri adalah lvalue dan   bahwa operan kanan adalah prvalue   dan menghasilkan lvalue sebagai hasilnya.   Operator yang ditentukan pengguna adalah fungsi,   dan kategori nilai-nilai mereka   mengharapkan dan menghasilkan ditentukan oleh   parameter dan jenis pengembalian mereka. -akhir   catatan

Saya sarankan Anda membaca seluruh bagian 3.10 Nilai dan r value meskipun.

Bagaimana kategori-kategori baru ini berhubungan dengan kategori rvalue dan lvalue yang ada?

Lagi:

Taxonomy

Apakah kategori rvalue dan lvalue di C ++ 0x sama dengan di C ++ 03?

Semantik rvalues ​​telah berkembang terutama dengan pengenalan semantik gerakan.

Mengapa kategori-kategori baru ini dibutuhkan?

Sehingga memindahkan konstruksi / tugas dapat ditentukan dan didukung.


303
2017-08-31 08:08



Saya akan mulai dengan pertanyaan terakhir Anda:

Mengapa kategori-kategori baru ini dibutuhkan?

Standar C ++ mengandung banyak aturan yang berhubungan dengan kategori nilai dari sebuah ekspresi. Beberapa aturan membuat perbedaan antara nilai dan rnilai. Misalnya, ketika datang ke resolusi yang berlebihan. Aturan lain membuat perbedaan antara glvalue dan prvalue. Sebagai contoh, Anda dapat memiliki glvalue dengan tipe yang tidak lengkap atau abstrak tetapi tidak ada prvalue dengan tipe yang tidak lengkap atau abstrak. Sebelum kita memiliki terminologi ini, aturan yang benar-benar perlu membedakan antara nilai / nilai praninjau merujuk pada nilai / rnilai dan mereka salah secara tidak sengaja atau mengandung banyak penjelasan dan pengecualian terhadap aturan a la "... kecuali rvalue yang tidak disebutkan namanya. rujukan referensi ... ". Jadi, sepertinya ide yang bagus untuk hanya memberikan konsep glvalu dan prvalues ​​nama mereka sendiri.

Apa kategori baru dari ekspresi ini?   Bagaimana kategori-kategori baru ini berhubungan dengan kategori rvalue dan lvalue yang ada?

Kami masih memiliki syarat lvalue dan rvalue yang kompatibel dengan C ++ 98. Kami hanya membagi nilai-nilai menjadi dua subkelompok, xvalues ​​dan prvalues, dan kami mengacu pada nilai dan xvalues ​​sebagai nilai glare. Xvalues ​​adalah jenis baru kategori nilai untuk referensi rvalue tanpa nama. Setiap ekspresi adalah salah satu dari tiga ini: lvalue, xvalue, prvalue. Diagram Venn akan terlihat seperti ini:

    ______ ______
   /      X      \
  /      / \      \
 |   l  | x |  pr  |
  \      \ /      /
   \______X______/
       gl    r

Contoh dengan fungsi:

int   prvalue();
int&  lvalue();
int&& xvalue();

Tetapi jangan lupa bahwa referensi rvalue yang bernama adalah lvalues:

void foo(int&& t) {
  // t is initialized with an rvalue expression
  // but is actually an lvalue expression itself
}

156
2018-03-04 06:32



Mengapa kategori-kategori baru ini dibutuhkan? Apakah para dewa WG21 hanya mencoba membuat kita bingung hanya manusia?

Saya tidak merasa bahwa jawaban yang lain (baik meskipun banyak dari mereka) benar-benar menangkap jawaban atas pertanyaan khusus ini. Ya, kategori ini dan itu ada untuk memungkinkan semantik bergerak, tetapi kompleksitas ada karena satu alasan. Ini adalah aturan yang tidak diganggu untuk memindahkan barang di C ++ 11:

Engkau harus bergerak hanya jika tidak diragukan lagi aman untuk melakukannya.

Itulah mengapa kategori-kategori ini ada: untuk dapat berbicara tentang nilai-nilai di mana aman untuk bergerak dari mereka, dan untuk berbicara tentang nilai-nilai di mana tidak.

Dalam versi referensi r-value yang paling awal, pergerakan terjadi dengan mudah. Terlalu dengan mudah. Cukup mudah bahwa ada banyak potensi untuk memindahkan secara implisit hal-hal ketika pengguna tidak benar-benar bermaksud.

Berikut adalah keadaan di mana aman untuk memindahkan sesuatu:

  1. Ketika itu sementara atau subobjek daripadanya. (prvalue)
  2. Ketika pengguna memiliki secara eksplisit mengatakan untuk memindahkannya.

Jika kamu melakukan ini:

SomeType &&Func() { ... }

SomeType &&val = Func();
SomeType otherVal{val};

Apa yang ini lakukan? Dalam versi yang lebih lama dari spesifikasi, sebelum 5 nilai masuk, ini akan memancing suatu langkah. Tentu saja. Anda melewati referensi rvalue ke konstruktor, dan dengan demikian ia mengikat konstruktor yang mengambil referensi rvalue. Itu sudah jelas.

Hanya ada satu masalah dengan ini; kamu tidak meminta untuk memindahkannya. Oh, Anda mungkin mengatakan bahwa && seharusnya menjadi petunjuk, tapi itu tidak mengubah fakta bahwa itu melanggar aturan. val bukan sementara karena sementara tidak punya nama. Anda mungkin telah memperpanjang masa pakai sementara, tetapi itu berarti tidak sementara; sama seperti variabel tumpukan lainnya.

Jika itu bukan sementara, dan Anda tidak meminta untuk memindahkannya, maka bergeraklah salah.

Solusi yang jelas adalah membuatnya val sebuah lvalue. Ini berarti Anda tidak bisa bergerak dari itu. Baiklah; itu bernama, jadi itu lvalue.

Setelah Anda melakukannya, Anda tidak bisa lagi mengatakan itu SomeType&& berarti hal yang sama di mana pun. Anda sekarang telah membuat perbedaan antara rvalue referensi bernama dan referensi rvalue tanpa nama. Nah, referensi rvalue bernama adalah lvalues; itu adalah solusi kami di atas. Jadi apa yang kita sebut referensi rvalue tanpa nama (nilai kembali dari Func atas)?

Ini bukan lvalue, karena Anda tidak bisa bergerak dari lvalue. Dan kita perlu untuk bisa bergerak dengan mengembalikan a &&; bagaimana lagi Anda bisa secara eksplisit mengatakan untuk memindahkan sesuatu? Itu adalah apa std::movekembali, setelah semua. Ini bukan rvalue (gaya lama), karena bisa di sisi kiri persamaan (sebenarnya ada yang lebih rumit, lihat pertanyaan ini dan komentar di bawah). Ini bukan nilai atau nilai; itu hal baru.

Apa yang kami miliki adalah nilai yang dapat Anda perlakukan sebagai lvalue, kecuali bahwa secara implisit dapat dipindahkan dari. Kami menyebutnya sebagai xvalue.

Perhatikan bahwa xvalues ​​adalah apa yang membuat kita mendapatkan dua kategori nilai lainnya:

  • Prvalue adalah benar-benar hanya nama baru untuk jenis rvalue sebelumnya, yaitu mereka adalah rvalues ​​itu tidak xvalues.

  • Nilai Gl adalah gabungan dari xvalues ​​dan lvalues ​​dalam satu grup, karena mereka berbagi banyak kesamaan properti.

Jadi benar-benar, semuanya bermuara pada xvalues ​​dan kebutuhan untuk membatasi pergerakan ke tepat dan hanya tempat-tempat tertentu. Tempat-tempat tersebut ditentukan oleh kategori rvalue; prvalues ​​adalah gerakan implisit, dan xvalues ​​adalah gerakan eksplisit (std::move mengembalikan xvalue).


130
2017-07-03 12:30



IMHO, penjelasan terbaik tentang maknanya memberi kami Stroustrup + memperhitungkan contoh akun Dániel Sándor dan Mohan:

Stroustrup:

Sekarang saya benar-benar khawatir. Jelas kami menuju kebuntuan atau   berantakan atau keduanya. Saya menghabiskan waktu makan siang untuk melakukan analisis untuk melihat mana   dari properti (nilai-nilai) yang independen. Hanya ada dua   sifat independen:

  • has identity - yaitu dan alamat, penunjuk, pengguna dapat menentukan apakah dua salinan identik, dll.
  • can be moved from - yaitu, kami diizinkan untuk pergi ke sumber "salinan" dalam keadaan yang tidak pasti, tetapi valid

Ini membawa saya pada kesimpulan bahwa ada tiga jenis persis   nilai-nilai (menggunakan trik notasi regex menggunakan huruf kapital untuk   menunjukkan negatif - saya sedang terburu-buru):

  • iM: memiliki identitas dan tidak dapat dipindahkan
  • im: memiliki identitas dan dapat dipindahkan dari (misalnya hasil pengecoran nilai ke rvalue reference)
  • Im: tidak memiliki identitas dan dapat dipindahkan dari kemungkinan keempat (IM: tidak memiliki identitas dan tidak dapat dipindahkan) tidak   berguna dalam C++ (atau, saya pikir) dalam bahasa lain.

Selain ketiga klasifikasi nilai fundamental ini, kami   memiliki dua generalisasi yang jelas yang sesuai dengan keduanya   sifat independen:

  • i: memiliki identitas
  • m: dapat dipindahkan dari

Ini membuat saya menempatkan diagram ini di papan tulis:    enter image description here

Penamaan

Saya mengamati bahwa kami hanya memiliki kebebasan terbatas untuk memberi nama: Dua poin untuk   sebelah kiri (berlabel iM dan i) adalah apa yang orang-orang dengan lebih atau kurang   formalitas telah disebut lvalues dan dua titik di sebelah kanan   (berlabel m dan Im) adalah apa yang orang-orang dengan formalitas lebih atau kurang   telah menelepon rvalues. Ini harus tercermin dalam penamaan kami. Itu adalah,   "kaki" kiri dari W harus memiliki nama yang terkait lvalue dan   "kaki" kanan W harus memiliki nama yang terkait rvalue. Saya perhatikan   bahwa seluruh diskusi / masalah ini muncul dari pengenalan   rvalue referensi dan pindahkan semantik. Gagasan ini tidak ada   di dunia Strachey yang terdiri dari adil rvalues dan lvalues. Some one   mengamati bahwa gagasan itu

  • Setiap value adalah salah satu lvalue atau sebuah rvalue
  • Sebuah lvalue bukan sebuah rvalue dan sebuah rvalue bukan sebuah lvalue 

sangat tertanam dalam kesadaran kita, sifat yang sangat berguna, dan   jejak dikotomi ini dapat ditemukan di seluruh standar draf. Kita   semua setuju bahwa kita harus melestarikan properti itu (dan membuatnya   tepat). Ini semakin membatasi pilihan penamaan kami. Saya mengamati itu   menggunakan kata-kata pustaka standar rvalue berarti m (itu   generalisasi), sehingga dapat mempertahankan harapan dan teks dari   perpustakaan standar titik bawah tangan kanan dari W harus diberi nama    rvalue.

Ini menyebabkan diskusi yang terfokus pada penamaan. Pertama, kami harus memutuskan   di lvalue. Harus lvalue berarti iM atau generalisasi i? LED   oleh Doug Gregor, kami membuat daftar tempat-tempat dalam susunan kata inti   dimana kata itu lvalue memenuhi syarat untuk berarti yang satu atau yang lain. SEBUAH   daftar dibuat dan dalam banyak kasus dan dalam teks yang paling rumit / rapuh    lvalue saat ini berarti iM. Ini adalah arti klasik dari nilai   karena "di masa lalu" tidak ada yang dipindahkan; move adalah gagasan baru   di C++0x. Juga, menamai titik puncak dari W  lvalue memberi kita   properti yang setiap nilai adalah sebuah lvalue atau sebuah rvalue, tetapi tidak keduanya.

Jadi, titik kiri atas dari W aku s lvalue dan titik kanan bawah   aku s rvalue. Apa yang membuat titik kiri bawah dan kanan atas?   Titik kiri bawah adalah generalisasi nilai klasik,   memungkinkan untuk bergerak. Jadi itu a generalized lvalue. Kami menamakannya    glvalue. Anda dapat berdalih tentang singkatan, tetapi (saya pikir) tidak   dengan logika. Kami berasumsi bahwa dalam penggunaan yang serius generalized lvalue   entah bagaimana akan disingkat, jadi kami lebih baik melakukannya   segera (atau risiko kebingungan). Titik kanan atas W kurang   umum dari kanan bawah (sekarang, seperti biasa, disebut) rvalue). Bahwa   titik mewakili gagasan murni asli dari suatu objek yang dapat Anda pindahkan   dari karena tidak bisa dirujuk lagi (kecuali oleh destructor).   Saya suka ungkapan itu specialized rvalue berlawanan dengan generalized lvalue tapi pure rvalue disingkat prvalue menang (dan   mungkin memang demikian). Jadi, kaki kiri W adalah lvalue dan    glvalue dan kaki kanannya prvalue dan rvalue. Kebetulan,   setiap nilai merupakan nilai atau prvalue, tetapi tidak keduanya.

Ini meninggalkan bagian tengah atas W: im; yaitu nilai-nilai yang dimiliki   identitas dan dapat dipindahkan. Kami benar-benar tidak memiliki panduan apa pun   kita untuk nama yang bagus untuk binatang esoterik itu. Mereka penting untuk   orang yang bekerja dengan teks standar (draft), tetapi tidak mungkin   menjadi nama rumah tangga. Kami tidak menemukan kendala nyata di   penamaan untuk memandu kami, jadi kami memilih ‘x’ untuk pusat, yang tidak diketahui,   aneh, hanya xpert, atau bahkan x-rated.

Steve showing off the final product


92
2018-01-20 13:46



PENGANTAR

ISOC ++ 11 (resmi ISO / IEC 14882: 2011) adalah versi terbaru dari standar bahasa pemrograman C ++. Ini berisi beberapa fitur baru, dan konsep, misalnya:

  • rujukan nilai
  • xvalue, glvalue, nilai kategori nilai prvalue
  • memindahkan semantik

Jika kita ingin memahami konsep kategori nilai ekspresi baru kita harus menyadari bahwa ada rvalue dan referensi nilai. Lebih baik untuk mengetahui rvalues ​​dapat diteruskan ke referensi rvalue non-const.

int& r_i=7; // compile error
int&& rr_i=7; // OK

Kita dapat memperoleh beberapa intuisi dari konsep kategori nilai jika kita mengutip subbagian berjudul Lvalues ​​dan rvalues ​​dari draft kerja N3337 (draft yang paling mirip dengan standar ISOC ++ 11 yang diterbitkan).

3.10 Nilai dan rujukan [dasar.Lval]

1 Ekspresi dikategorikan menurut taksonomi pada Gambar 1.

  • Nilai lv (disebut, secara historis, karena nilai dapat muncul di sisi kiri ekspresi penugasan) menunjuk fungsi   atau sebuah objek. [Contoh: Jika E adalah ekspresi dari tipe pointer, maka   * E adalah ekspresi lvalue yang mengacu pada objek atau fungsi yang menjadi titik E. Sebagai contoh lain, hasil pemanggilan fungsi   yang jenis kembalinya adalah referensi lnilai adalah lvalue. —selesai contoh]
  • Nilai x (nilai "eXpiring") juga mengacu pada objek, biasanya mendekati akhir masa pakainya (sehingga sumber dayanya dapat dipindahkan, untuk   contoh). Nilai x adalah hasil dari beberapa jenis ekspresi tertentu   melibatkan rujukan nilai (8.3.2). [Contoh: Hasil panggilan   fungsi yang tipe kembalinya adalah rvalue reference adalah xvalue. -akhir   contoh]
  • Nilai gla (nilai "umum") adalah nilai atau nilai x.
  • Sebuah rvalue (disebut, secara historis, karena rvalues ​​dapat muncul di sisi kanan ekspresi tugas) adalah nilai x, sebuah
      objek sementara (12.2) atau subobjek daripadanya, atau nilai yang tidak
      terkait dengan suatu objek.
  • Nilai prabayar (nilai "murni") adalah rvalue yang bukan merupakan nilai x. [Contoh: Hasil memanggil fungsi yang jenis kembalinya bukan a
      referensi adalah prvalue. Nilai literal seperti 12, 7.3e5, atau
      benar juga merupakan prvalue. —selesai contoh]

Setiap ungkapan termasuk salah satu yang fundamental   klasifikasi dalam taksonomi ini: lvalue, xvalue, atau prvalue. Ini   properti ekspresi disebut kategori nilainya.

Tapi saya tidak cukup yakin bahwa subbagian ini cukup untuk memahami konsep dengan jelas, karena "biasanya" tidak benar-benar umum, "menjelang akhir masa hidupnya" tidak benar-benar konkret, "melibatkan rujukan nilai" tidak terlalu jelas, dan "Contoh: Hasil pemanggilan fungsi yang jenis kembalinya adalah rvalue referensi adalah xvalue." Terdengar seperti ular yang menggigit ekornya.

KATEGORI NILAI PRIMER

Setiap ekspresi milik tepat satu kategori nilai utama. Kategori nilai ini adalah kategori nilai, xvalue dan prvalue.

nilai-nilai

Ekspresi E termasuk kategori lvalue jika dan hanya jika E mengacu pada entitas yang SUDAH memiliki identitas (alamat, nama atau alias) yang membuatnya dapat diakses di luar E.

#include <iostream>

int i=7;

const int& f(){
    return i;
}

int main()
{
    std::cout<<&"www"<<std::endl; // This address ...
    std::cout<<&"www"<<std::endl; // ... and this address are the same.
    "www"; // The expression "www" in this row is an lvalue expression, because it refers to the same entity ...
    "www"; // ... as the entity the expression "www" in this row refers to.

    i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression i in this row refers to.

    int* p_i=new int(7);
    *p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
    *p_i; // ... as the entity the expression *p_i in this row refers to.

    const int& r_I=7;
    r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
    r_I; // ... as the entity the expression r_I in this row refers to.

    f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression f() in this row refers to.

    return 0;
}

xvalues

Ekspresi E milik kategori xvalue jika dan hanya jika itu

- hasil pemanggilan fungsi, baik secara implisit maupun eksplisit, yang jenis kembalinya adalah rujukan referensi untuk jenis objek yang dikembalikan, atau

int&& f(){
    return 3;
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.

    return 0;
}

- Pemeran ke referensi rvalue ke jenis objek, atau

int main()
{
    static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
    std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).

    return 0;
}

- ekspresi akses anggota kelas yang menunjuk anggota data non-statis dari jenis non-referensi di mana ekspresi objek adalah xvalue, atau

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.

    return 0;
}

- ekspresi pointer-ke-anggota di mana operan pertama adalah nilai x dan operan kedua adalah penunjuk ke anggota data.

Perhatikan bahwa efek dari aturan di atas adalah yang diberi nama rvalue referensi ke objek diperlakukan sebagai lvalues ​​dan referensi rvalue tanpa nama ke objek diperlakukan sebagai xvalues; rvalue referensi ke fungsi diperlakukan sebagai nilai-nilai apakah bernama atau tidak.

#include <functional>

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
    As&& rr_a=As();
    rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
    std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.

    return 0;
}

prvalues

Ekspresi E termasuk dalam kategori prvalue jika dan hanya jika E tidak termasuk nilai lvalue atau kategori xvalue.

struct As
{
    void f(){
        this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
    }
};

As f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.

    return 0;
}

KATEGORI MIXED VALUE

Ada dua kategori nilai campuran yang lebih penting. Kategori nilai ini merupakan kategori rvalue dan glvalue.

rvalues

Ekspresi E termasuk kategori rvalue jika dan hanya jika E termasuk dalam kategori xvalue, atau kategori prvalue.

Perhatikan bahwa definisi ini berarti bahwa ekspresi E termasuk kategori rvalue jika dan hanya jika E mengacu pada entitas yang belum memiliki identitas yang membuatnya dapat diakses di luar EET.

glalue

Ekspresi E termasuk kategori glvalue jika dan hanya jika E termasuk kategori lvalue, atau kategori xvalue.

ATURAN PRAKTIS

Scott Meyer punya diterbitkan aturan yang sangat berguna untuk membedakan rvalu dari nilai-nilai.

  • Jika Anda dapat mengambil alamat ekspresi, ekspresi adalah nilai.
  • Jika jenis ekspresi adalah referensi lnilai (misalnya, T & atau const T &, dll.), Ekspresi itu adalah lvalue.
  • Jika tidak, ungkapan itu adalah rvalue. Secara konseptual (dan biasanya juga sebenarnya), rvalues ​​sesuai dengan objek sementara, seperti itu   seperti yang dikembalikan dari fungsi atau dibuat melalui tipe implisit   konversi. Sebagian besar nilai literal (misalnya, 10 dan 5.3) juga merupakan nilai rujukan.

34
2017-08-30 16:46



Kategori C ++ 03 terlalu terbatas untuk menangkap pengenalan rvalue referensi dengan benar ke dalam atribut ekspresi.

Dengan diperkenalkannya mereka, dikatakan bahwa referensi rvalue yang tidak disebutkan namanya mengevaluasi ke rvalue, sehingga resolusi yang berlebihan akan lebih memilih rujukan referensi rnalue, yang akan membuatnya memilih pindahkan konstruktor ke over copy constructor. Tetapi ditemukan bahwa ini menyebabkan masalah di sekitar, misalnya dengan Jenis Dinamis dan dengan kualifikasi.

Untuk menunjukkan ini, pertimbangkan

int const&& f();

int main() {
  int &&i = f(); // disgusting!
}

Pada konsep pra-xvalue, ini diizinkan, karena pada C ++ 03, nilai rujukan dari jenis non-kelas tidak pernah memenuhi syarat. Tetapi memang dimaksudkan demikian const berlaku dalam kasus rvalue-referensi, karena di sini kita melakukan lihat objek (= memori!), dan menjatuhkan konst dari nilai-nilai non-kelas terutama karena tidak ada objek di sekitarnya.

Masalah untuk tipe dinamis memiliki sifat yang serupa. Dalam C ++ 03, ralue tipe kelas memiliki tipe dinamis yang diketahui - ini adalah jenis statis dari ekspresi tersebut. Karena memilikinya dengan cara lain, Anda perlu referensi atau dereferensi, yang mengevaluasi ke nilai lv. Itu tidak benar dengan referensi rvalue tanpa nama, namun mereka dapat menunjukkan perilaku polimorfik. Jadi untuk menyelesaikannya,

  • referensi rvalue tanpa nama menjadi xvalues. Mereka dapat memenuhi syarat dan berpotensi memiliki jenis dinamis mereka yang berbeda. Mereka, seperti yang dimaksudkan, lebih menyukai rvalue referensi selama overloading, dan tidak akan mengikat ke referensi non-const lvalue.

  • Apa yang sebelumnya merupakan rvalue (literal, objek yang dibuat oleh pemain untuk jenis non-referensi) sekarang menjadi prvalue. Mereka memiliki preferensi yang sama dengan xvalues ​​selama overloading.

  • Apa yang sebelumnya merupakan nilai lvalue tetap merupakan lvalue.

Dan dua pengelompokan dilakukan untuk menangkap mereka yang dapat memenuhi syarat dan dapat memiliki jenis dinamis yang berbeda (glalue) dan mereka yang overloading lebih menyukai pengikatan rujukan referensi (rvalues).


32
2018-06-17 02:20



Saya telah berjuang dengan ini untuk waktu yang lama, sampai saya menemukan penjelasan cppreference.com tentang kategori nilai.

Sebenarnya agak sederhana, tetapi saya menemukan bahwa itu sering dijelaskan dengan cara yang sulit untuk diingat. Di sini dijelaskan secara skematis. Saya akan mengutip beberapa bagian halaman:

Kategori utama

Kategori nilai utama sesuai dengan dua properti ekspresi:

  • memiliki identitas: mungkin untuk menentukan apakah ekspresi mengacu pada entitas yang sama dengan ekspresi lain, seperti dengan membandingkan alamat objek atau fungsi yang mereka identifikasi (diperoleh secara langsung atau tidak langsung);

  • dapat dipindahkan dari: memindahkan konstruktor, memindahkan operator penugasan, atau fungsi lain yang berlebihan yang mengimplementasikan semantik gerakan dapat berikatan dengan ekspresi.

Ekspresi yang:

  • memiliki identitas dan tidak dapat dipindahkan dari yang dipanggil ekspresi nilai;
  • memiliki identitas dan dapat dipindahkan dari yang dipanggil Ekspresi xvalue;
  • tidak memiliki identitas dan dapat dipindahkan dari yang dipanggil ekspresi prvalue;
  • tidak memiliki identitas dan tidak dapat dipindahkan tidak digunakan.

lvalue

Nilai lvalue ("nilai kiri") adalah ekspresi yang memiliki identitas dan tidak dapat dipindahkan.

rvalue (hingga C ++ 11), prvalue (sejak C ++ 11)

Nilai prvalue ("murni r value") adalah ekspresi itu tidak memiliki identitas dan dapat dipindahkan dari.

xvalue

Ekspresi xvalue ("expaten value") adalah ekspresi yang memiliki identitas dan dapat dipindahkan dari.

glvalue

Nilai glvalue ("general value lvalue") adalah ekspresi yang bernilai lvalue atau xvalue. Saya t memiliki identitas. Mungkin atau mungkin tidak dipindahkan.

rvalue (sejak C ++ 11)

Nilai rvalue ("nilai yang benar") adalah ekspresi yang merupakan nilai prvalue atau xvalue. Saya t dapat dipindahkan dari. Itu mungkin atau mungkin tidak memiliki identitas.


20
2017-08-30 15:45



Bagaimana kategori-kategori baru ini berhubungan dengan kategori rvalue dan lvalue yang ada?

Nilai C ++ 03 masih bernilai C ++ 11, sedangkan nilai C ++ 03 disebut prvalue dalam C ++ 11.


15
2017-07-25 04:26



Satu tambahan jawaban yang sangat bagus di atas, pada titik yang membingungkan saya bahkan setelah saya membaca Stroustrup dan berpikir saya memahami perbedaan nilai / lvalue. Ketika kamu melihat

int&& a = 3,

sangat menggoda untuk membaca int&& sebagai tipe dan menyimpulkan itu a adalah rvalue. Ini bukan:

int&& a = 3;
int&& c = a; //error: cannot bind 'int' lvalue to 'int&&'
int& b = a; //compiles

a memiliki nama dan adalah ipso facto suatu lvalue. Jangan pikirkan tentang itu && sebagai bagian dari jenis a; itu hanya sesuatu yang memberitahumu apa a diizinkan untuk mengikat.

Ini penting terutama untuk T&& ketik argumen dalam konstruktor. Jika Anda menulis

Foo::Foo(T&& _t) : t{_t} {}

Anda akan menyalin _t ke t. Anda butuh

Foo::Foo(T&& _t) : t{std::move(_t)} {} jika kamu ingin pindah. Apakah kompiler saya memperingatkan saya ketika saya meninggalkan move!


12
2018-04-21 06:58



Dalam C ++, variabel adalah tipe l-value (nilai ell diucapkan). Nilai-l adalah nilai yang memiliki alamat tetap (dalam memori). Karena semua variabel memiliki alamat, semua variabel adalah nilai-l. Nilai-l nama muncul karena l-nilai adalah satu-satunya nilai yang dapat berada di sisi kiri pernyataan tugas. Saat kami melakukan tugas, sisi kiri operator penugasan harus berupa nilai-l. Akibatnya, pernyataan seperti 5 = 6; akan menyebabkan kesalahan kompilasi, karena 5 bukan merupakan nilai-l. Nilai 5 tidak memiliki memori, dan dengan demikian tidak ada yang dapat ditugaskan untuk itu. 5 berarti 5, dan nilainya tidak dapat dipindahkan. Ketika nilai-l memiliki nilai yang ditetapkan untuk itu, nilai saat ini di alamat memori itu ditimpa.

Kebalikan dari nilai-l adalah r-nilai (diucapkan nilai-arr). Nilai-r mengacu pada nilai-nilai yang tidak terkait dengan alamat memori persisten. Contoh nilai r adalah angka tunggal (seperti 5, yang mengevaluasi hingga 5) dan ekspresi (seperti 2 + x, yang mengevaluasi nilai variabel x tambah 2). r-nilai umumnya bersifat sementara dan dibuang di akhir pernyataan di mana mereka terjadi.

Berikut ini contoh beberapa pernyataan penugasan, yang menunjukkan bagaimana nilai-r mengevaluasi:

int y;      // define y as an integer variable
y = 4;      // r-value 4 evaluates to 4, which is then assigned to l-value y
y = 2 + 5;  // r-value 2 + r-value 5 evaluates to r-value 7, which is then assigned to l-value y

int x;      // define x as an integer variable
x = y;      // l-value y evaluates to 7 (from before), which is then assigned to l-value x.
x = x;      // l-value x evaluates to 7, which is then assigned to l-value x (useless!)
x = x + 1;  // l-value x + r-value 1 evaluate to r-value 8, which is then assigned to l-value x.

Mari kita lihat lebih dekat pernyataan tugas terakhir di atas, karena ini menyebabkan kebingungan paling besar.

x = x+1

Dalam pernyataan ini, variabel x sedang digunakan dalam dua konteks yang berbeda. Di sisi kiri operator penugasan, "x" digunakan sebagai l-nilai (variabel dengan alamat) untuk menyimpan nilai. Di sisi kanan operator penugasan, x dievaluasi untuk menghasilkan nilai (dalam hal ini, 7). Ketika C ++ mengevaluasi pernyataan di atas, ia mengevaluasi sebagai:

x = 7+1

Yang membuatnya jelas bahwa C + + akan memberikan nilai 8 kembali ke variabel x.

Untuk saat ini, Anda tidak perlu khawatir tentang nilai-l atau nilai-r banyak, tetapi kami akan kembali lagi nanti ketika kami mulai membahas beberapa topik lanjutan.

Kunci takeaway di sini adalah bahwa di sisi kiri penugasan, Anda harus memiliki sesuatu yang mewakili alamat memori (seperti variabel). Segala sesuatu di sisi kanan tugas akan dievaluasi untuk menghasilkan nilai.


-2