Değerler, değerler, xvalues, glvalues ​​ve prvalues ​​nedir?


1356

C ++ 03'te bir ifade ya bir değerdir ya da bir değerdir .

C ++ 11'de bir ifade aşağıdakiler olabilir:

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

İki kategori beş kategori haline gelmiştir.

  • Bu yeni ifade kategorileri nelerdir?
  • Bu yeni kategoriler mevcut değer ve değer kategorileriyle nasıl ilişkilidir?
  • C ++ 0x'deki rvalue ve lvalue kategorileri, C ++ 03'teki ile aynı mıdır?
  • Bu yeni kategorilere neden ihtiyaç duyuluyor? Are WG21 tanrılar bizi ölümlüler karıştırmaya çalışıyorlar?

9
@Philip Potter: C ++ 03'te mi? Evet. Bir lvalue bir rvalue olarak kullanılabilir, çünkü standart bir lvalue-rvalue dönüşümü vardır.
James McNellis

14
@Tyler: "Eğer atayabilirseniz, bu bir değerdir, aksi takdirde bir değerdir." -> Yanlış, sınıf SağDeğerler için atayabilirsiniz: string("hello") = string("world").
fredoverflow

4
Bunun değer kategorisi olduğunu unutmayın. İfadelerin sahip olabileceği daha fazla özellik var. Bunlar bit-alanı (doğru / yanlış), geçici (doğru / yanlış) ve türü (türünü) içerir.
Johannes Schaub - litb

30
Bence Fred'in yukarıdaki linki buradaki cevaplardan daha iyi. Bağlantı kopmuş olsa da. Taşıdı
R. Martinho Fernandes

74
C ++ 'da türlerinizin bile türleri var
nielsbot

Yanıtlar:


634

Sanırım bu belge çok kısa bir giriş değil: n3055

Bütün katliam, hareket semantiği ile başladı. Hareket ettirilip kopyalanamayan ifadeler elde ettiğimizde, kuralları kavramak birdenbire kolayca hareket ettirilebilen ifadeler ile hangi yönde ayrım yapılmasını istedi.

Taslağa dayanarak tahmin ettiğimden, r / l değer ayrımı aynı kalır, sadece hareketli şeyler bağlamında dağınık hale gelir.

İhtiyaçları var mı? Muhtemelen yeni özellikleri kaybetmek istiyorsak değil. Ancak daha iyi bir optimizasyon sağlamak için muhtemelen bunları benimsememiz gerekir.

Alıntı n3055 :

  • Bir lvalue (tarihsel olarak denir, çünkü atama ifadesinin sol tarafında lvalues ​​görünebilir) bir işlevi veya nesneyi belirtir. [Örnek: Eİşaretçi türünün bir ifadesiyse, işaret *E eden nesneye veya işleve atıfta bulunan bir değer değeri ifadesidir E . Başka bir örnek olarak, dönüş türü bir değer referansı olan bir işlevi çağırmanın sonucu bir değerdir.]
  • Bir xvalue (bir "eXpiring" değeri), genellikle kullanım ömrünün sonuna yakın bir nesneyi de ifade eder (örneğin, kaynaklarının taşınabilmesi için). Xvalue, rvalue referanslarını içeren belirli türdeki ifadelerin sonucudur. [Örnek: Dönüş türü rvalue referansı olan bir işlevi çağırmanın sonucu xvalue'dur.]
  • Bir glvalue (lvalue “genel”) bir olduğu lvalue veya xValue .
  • Bir rvalue (SağDeğerler bir atama ifadesinin sağ tarafta görünür, çünkü, tarihsel olarak adlandırılan) bir xValue, geçici bir nesne ya da subobject veya bunların bir nesne ile ilişkili olmayan bir değerdir.
  • Bir değer (“saf” değer) x değeri olmayan bir değerdir. [Örnek: Dönüş türü başvuru olmayan bir işlevi çağırmanın sonucu bir ön değerdir]

Söz konusu belge bu soru için harika bir referanstır, çünkü yeni isimlendirmenin başlatılması sonucunda meydana gelen standarttaki kesin değişiklikleri gösterir.


Teşekkürler, bu cevap gerçekten yararlı! Ancak derleyicim xvalues ​​ve prvalues ​​örneklerinizle aynı fikirde değil; tam tersi. Rvalue referansı ile dönmek bana bir değer verir ve değere göre dönmek bana bir xvalue verir. Onları karıştırdın mı yoksa test yatağım kırıldı mı? Bunu GCC 4.6.1, clang (svn'den) ve MSVC ile denedim ve hepsi aynı davranışı gösteriyor.
Kim Gräsman

Hata, sadece bağlantıyı takip ettim ve örneklerin kaynakta olduğunu fark ettim. Ben standardın bir kopyasını bulacağım ve ne dediğini kontrol
edeceğim

4
Buradan çeşitli ifadeleri test etmek için makroları kullanıyorum: stackoverflow.com/a/6114546/96963 Bazı şeyleri yanlış teşhis edebilirler.
Kim Gräsman

1
Xvalue eklemek hareket semantiği için değildir. Sadece hem değer hem de değer ile, hareket semantiği, mükemmel ileri ve değer referansı hala iyi çalışır. Ben xvalue sadece decltype operatörü için olduğunu düşünüyorum: işlenen ifade xvalue ise, decltype rvalue başvuru türünü verir.
ligand

1
@MuhamedCicak "Her ifade ya bir değer ya da değerdir": bu doğrudur; ve standart (veya n3055 belgesi) bunun yanlış olduğunu söylemez. Bu cümlenin çizilmesinin nedeni, belgenin iki sürümü arasındaki değişikliklere bakmanızdı. Cümle daha kesin bir açıklama eklendikten sonra gereksiz hale geldiğinden kaldırıldı.
max

337

Bu yeni ifade kategorileri nelerdir?

FKH (n3092) mükemmel bir açıklama vardır:

- Bir değer (tarihsel olarak adlandırılır, çünkü ödev ifadesinin sol tarafında değerler görünebilir) bir işlevi veya nesneyi belirtir. [Örnek: E, işaretçi türünün bir ifadesiyse, * E, E'nin işaret ettiği nesneye veya işleve başvuran bir lvalue ifadesidir. Başka bir örnek olarak, dönüş türü bir değer referansı olan bir işlev çağrılmasının sonucu bir değerdir. —End örneği]

- Bir xvalue (bir "eXpiring" değeri), genellikle kullanım ömrünün sonuna yakın bir nesneyi de ifade eder (örneğin, kaynaklarının taşınabilmesi için). Xvalue, rvalue referanslarını içeren belirli ifade türlerinin sonucudur (8.3.2). [Örnek: Dönüş türü rvalue başvurusu olan bir işlevi çağırmanın sonucu xvalue'dur. —End örneği]

- Bir glvalue (“genelleştirilmiş” lvalue) bir lvalue veya xvalue'dur.

- Bir değer (geçmiş olarak adlandırılır, çünkü ödev ifadelerinin sağ tarafında değerler görünebilir) bir x değeri, geçici bir nesne (12.2) veya alt nesnesi veya bir nesne ile ilişkili olmayan bir değerdir.

- Bir değer (“saf” değer) x değeri olmayan bir değerdir. [Örnek: Dönüş türü başvuru olmayan bir işlevi çağırmanın sonucu bir ön değerdir. 12, 7.3e5 veya true gibi bir değişmez değerin değeri de bir değerdir. —End örneği]

Her ifade, bu sınıflandırmadaki temel sınıflandırmalardan birine aittir: lvalue, xvalue veya prevalue. Bir ifadenin bu özelliğine değer kategorisi denir. [Not: Madde 5'teki her yerleşik operatörün tartışması, verdiği değerin kategorisini ve beklediği işlenenlerin değer kategorilerini belirtir. Örneğin, yerleşik atama işleçleri, sol işlenenin bir değer ve sağ işlenenin bir ön değer olmasını ve sonuç olarak bir değer vermesini bekler. Kullanıcı tanımlı işleçler işlevlerdir ve bekledikleri ve verdikleri değer kategorileri, parametre ve dönüş türlerine göre belirlenir. —End not

Yine de 3.10 Lvalues ​​ve rvalues bölümünün tamamını okumanızı tavsiye ederim .

Bu yeni kategoriler mevcut değer ve değer kategorileriyle nasıl ilişkilidir?

Tekrar:

Taksonomi

C ++ 0x'deki rvalue ve lvalue kategorileri, C ++ 03'teki ile aynı mıdır?

Değerlerin semantiği özellikle hareket semantiğinin ortaya çıkmasıyla gelişmiştir.

Bu yeni kategorilere neden ihtiyaç duyuluyor?

Böylece bu hareket inşası / ataması tanımlanabilir ve desteklenebilir.


54
Buradaki diyagramı seviyorum. Cevabı "Her ifade, bu sınıflandırmadaki temel sınıflandırmalardan birine aittir: lvalue, xvalue veya prevalue" ile başlamanın yararlı olabileceğini düşünüyorum . Daha sonra diyagramı kullanmak, bu üç temel sınıfın, değerleme ve değerleme yapmak için birleştirildiğini göstermek için kullanılır.
Aaron McDaid

2
"eşitliktir" eşittir "değer değildir" ve "eşittir" eşittir "değer değildir".
Vladimir Reshetnikov

2
Bu bana en çok yardımcı oldu: bajamircea.github.io/assets/2016-04-07-move-forward/… (değer kategorilerinin Venn diyagramı)
John P

1
@AaronMcDaid Merhaba, hızlı soru / Birisi cevap verebilir eğer ... Neden adı değil glvalueolarak lvalueve lvaluesıra plvalue, tutarlı olmak?
Vijay Chavda

184

Son sorunuzla başlayacağım:

Bu yeni kategorilere neden ihtiyaç duyuluyor?

C ++ standardı, bir ifadenin değer kategorisiyle ilgilenen birçok kural içerir. Bazı kurallar lvalue ve rvalue arasında bir ayrım yapar. Örneğin, aşırı yük çözünürlüğü söz konusu olduğunda. Diğer kurallar, glvalue ve prevalue arasında bir ayrım yapar. Örneğin, eksik veya soyut bir türle bir glvalue'nuz olabilir, ancak eksik veya soyut bir türle bir öndeğer yoktur. Bu terminolojiye sahip olmadan önce, aslında değer / değer değerine atıfta bulunulan glvalue / prevalue arasında ayrım yapılması gereken kurallar ve bunlar istemeden yanlıştı ya da a la kuralına çok fazla açıklama ve istisna içermiyordu ... ... rvalue referansı ... ". Yani, sadece değerlik ve değerlik kavramlarına kendi adlarını vermek iyi bir fikir gibi görünüyor.

Bu yeni ifade kategorileri nelerdir? Bu yeni kategoriler mevcut değer ve değer kategorileriyle nasıl ilişkilidir?

Hala C ++ 98 ile uyumlu lvalue ve rvalue terimlerine sahibiz. Değerleri sadece iki alt gruba, x değerlerine ve değerlere böldük ve değerlere ve x değerlerine değer olarak değindik. Xvalues, adsız rvalue referansları için yeni bir değer kategorisi türüdür. Her ifade şu üç taneden biridir: lvalue, xvalue, prevalue. Bir Venn şeması şöyle görünür:

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

Fonksiyonlu örnekler:

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

Ancak, adlandırılmış rvalue referanslarının lvalues ​​olduğunu da unutmayın:

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

165

Bu yeni kategorilere neden ihtiyaç duyuluyor? WG21 tanrıları sadece ölümlüleri karıştırmaya mı çalışıyorlar?

Diğer cevapların (birçoğu iyi olsa da) bu özel sorunun cevabını gerçekten yakaladığını hissetmiyorum. Evet, bu kategoriler ve benzerleri hareket semantiğine izin vermek için var, ancak karmaşıklık bir nedenden dolayı var. Bu, C ++ 11'deki şeyleri hareket ettirmenin tek inviolate kuralıdır:

Sadece tartışmasız güvenli olduğunda hareket edeceksin.

Bu yüzden bu kategoriler var: onlardan taşınmanın güvenli olduğu değerler hakkında konuşabilmek ve olmadığı yerlerde değerler hakkında konuşabilmek.

R-değeri referanslarının en eski versiyonunda, hareket kolayca gerçekleşti. Çok kolay. Kolayca, kullanıcı gerçekten demek istemediğinde örtük olarak hareket eden şeyler için çok fazla potansiyel vardı.

Bir şeyi taşımanın güvenli olduğu koşullar şunlardır:

  1. Geçici veya alt nesnesi olduğunda. (Prvalue)
  2. Kullanıcı açıkça taşımak için söylediğinde .

Eğer bunu yaparsan:

SomeType &&Func() { ... }

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

Bu ne yapar? Spesifikasyonun eski versiyonlarında, 5 değer girmeden önce, bu bir harekete neden olacaktır. Tabii ki öyle. Yapıcıya bir rvalue referansı geçtiniz ve böylece bir rvalue referansı alan yapıcıya bağlanır. Bu çok açık.

Bununla ilgili tek bir sorun var; Eğer vermedi sormak taşımak için. Oh, bunun &&bir ipucu olması gerektiğini söyleyebilirsiniz , ancak bu, kuralı ihlal ettiği gerçeğini değiştirmez. valgeçici değildir, çünkü geçicilerin isimleri yoktur. Geçici ömrünü uzatılabilir olabilir, ama o vasıta değil , geçici ; diğer yığın değişkenleri gibi.

Bu geçici değilse ve onu taşımak istemediyseniz, hareket etmek yanlıştır.

Açık çözüm bir değer yaratmaktır val. Bu, ondan hareket edemeyeceğiniz anlamına gelir. Tamam iyi; adlandırıldı, bu yüzden bir değer.

Bunu yaptıktan sonra, bunun SomeType&&her yerde aynı anlama geldiğini söyleyemezsiniz . Artık adlandırılmış değer referansları ile adsız değer referansları arasında bir ayrım yaptınız. Adı verilen değer referansları değerlerdir; yukarıdaki çözümümüz buydu. Peki adsız rvalue referanslarına ne denir ( Funcyukarıdan dönüş değeri )?

Bu bir lvalue değil, çünkü bir lvalue'dan hareket edemezsiniz. Ve dönerek hareket edebilmemiz gerekir&& ; bir şeyi taşımak için başka nasıl açıkça söyleyebilirsin? Ne de olsa std::movegeri dönüyor. Bu bir değer (eski tarz) değildir, çünkü bir denklemin sol tarafında olabilir (işler aslında biraz daha karmaşıktır, bu soruya ve aşağıdaki yorumlara bakınız). Ne bir değer ne de bir değerdir; yeni bir şey.

Sahip olduğumuz şey, dolaylı olarak hareket edebilmesinin dışında , bir değer olarak değerlendirebileceğiniz bir değerdir . Buna xvalue diyoruz.

X değerlerinin diğer iki değer kategorisini kazanmamızı sağlayan şey olduğunu unutmayın:

  • Bir ön değer, gerçekte yalnızca önceki türdeki değer için yeni addır, yani bunlar xvalues olmayan değerlerdir.

  • Glvalues, bir gruptaki xvalue ve lvalues ​​birliğidir, çünkü ortak bir çok özelliği paylaşırlar.

Yani gerçekten, her şey xvalues ​​ve hareketi tam ve sadece belirli yerlerle sınırlama ihtiyacı ile ilgilidir. Bu yerler rvalue kategorisine göre tanımlanır; öndeğerler örtük hareketlerdir ve xvalues ​​açık hareketlerdir ( std::movebir xvalue döndürür).


11
@Thomas: Bu bir örnek; dönüş değerini nasıl oluşturduğu önemli değildir. Önemli olan a döndürmesi &&.
Nicol Bolas

1
Not: eşitlikler bir denklemin sol tarafında da olabilir - olduğu gibi X foo(); foo() = X;... Bu temel nedenden ötürü, yukarıdaki mükemmel cevabı sonuna kadar takip edemem, çünkü gerçekten sadece lhs üzerinde olabileceği gerçeğine dayanarak yeni xvalue ve eski stil prevalue.
Dan Nissenbaum

1
Xsınıf olmak; X foo();bir işlev bildirimi ve foo() = X();bir kod satırıdır. ( foo() = X();Yukarıdaki yorumda ikinci parantez kümesini bıraktım.) Bu kullanımla vurgulandığım bir soru için, bkz. Stackoverflow.com/questions/15482508/…
Dan Nissenbaum 18:13

1
@DanNissenbaum "xvalue, atama ifadesinin sol tarafında olamaz" - neden olmasın? Bakınız ideone.com/wyrxiT
Mikhail

1
Aydınlatıcı cevap. Kuşkusuz buradaki en iyi cevap bu. Yeni değer kategorilerini ve daha önce neler olduğunu tanıtmamın mantığını verdi.
Nikos

136

Anlamıyla ilgili en iyi açıklama IMHO bize Stroustrup + 'ı Dániel Sándor ve Mohan'un örneklerini dikkate alarak verdi :

Stroustrup:

Şimdi çok endişelendim. Açıkçası biz bir çıkmaz ya da bir karmaşa ya da her ikisi için yöneldi. Öğle yemeğini, hangi özelliklerin (değerlerin) bağımsız olduğunu görmek için bir analiz yaparak geçirdim. Sadece iki bağımsız özellik vardı:

  • has identity - yani ve adres, bir işaretçi, kullanıcı iki kopyanın aynı olup olmadığını belirleyebilir, vb.
  • can be moved from - yani bazı belirsiz, ancak geçerli bir durumda "kopya" kaynağına gitmemize izin verilir

Bu beni tam olarak üç çeşit değer olduğu sonucuna götürdü (bir negatif harfi belirtmek için büyük harf kullanmanın normal ifade numarasını kullanarak - Acelem vardı):

  • iM: kimliği vardır ve taşınamaz
  • im: kimliğe sahip ve taşınabiliyor (örneğin, bir değer vermenin bir değer referansına dökülmesi sonucu)
  • Im: kimliği yoktur ve buradan taşınabilir.

    Dördüncü olasılık,, IM(kimliği yoktur ve hareket ettirilemez) C++başka bir dilde yararlı değildir (ya da sanırım).

Bu üç temel değer sınıflandırmasına ek olarak, iki bağımsız özelliğe karşılık gelen iki belirgin genellememiz var:

  • i: kimliği var
  • m: taşınabiliyor

Bu da bu şemayı tahtaya koymamı sağladı: resim açıklamasını buraya girin

Adlandırma

İsimlendirme özgürlüğümüzün sınırlı olduğunu gözlemledim: Soldaki iki nokta (etiketli iMve i) az çok formaliteye sahip insanların çağırdığı lvaluesve sağdaki iki nokta (etiketli mve Im) az çok formaliteye sahip insanların aradım rvalues. Bu bizim ismimize yansıtılmalıdır. Yani, sol "bacağı" ile Wilgili isimler lvalueve sağ "bacağı" ile Wilgili isimler olmalıdır rvalue.ben bu tüm tartışma / sorun rvalue referansları ve hareket semantik tanıtımı kaynaklanmaktadır dikkat edin. Bu kavramlar Strachey'in dünyasında adil rvaluesve oluşanlardan oluşmaz lvalues. Birisi gözlemledi ki fikirler

  • Her valuebiri bir lvalueya da birrvalue
  • An lvaluebir değil rvalueve rvaluebirlvalue

bilincimize derinden gömülüdürler, çok kullanışlı özellikleri vardır ve bu ikilemin izleri taslak standardın her yerinde bulunabilir. Hepimiz bu özellikleri korumamız (ve bunları kesinleştirmemiz) gerektiği konusunda hemfikirdik. Bu, adlandırma seçimlerimizi daha da kısıtladı. Standart kütüphane ifadelerinin anlamlandırma (genelleme) rvalueiçin kullandığını gözlemledim, böylece standart kütüphanenin mbeklenti ve metnini korumak için sağ alt noktaya Wisim verilmelidir. rvalue.

Bu, isimlendirme üzerine odaklanmış bir tartışmaya yol açtı. Öncelikle, karar için gerekli lvalue.Meli lvalueortalama iMveya genelleme i? Doug Gregor liderliğindeki kelimede lvalue, biri ya da diğeri anlamına gelecek nitelikte temel dilde ifade edilen yerleri listeledik . Bir liste yapıldı ve çoğu durumda ve en zor / kırılgan metin lvalueşu anda anlamına geliyor iM. Lvalue'nun klasik anlamı budur, çünkü "eski günlerde" hiçbir şey hareket etmemiştir; moveyeni bir kavram C++0x. Ayrıca, üst tarafa nokta ismini W lvaluevermek bize her bir değerin bir lvalueya da bir olduğunu rvalue, ancak her ikisini birden değil özelliği verir .

Yani, üst noktasını terk Wolduğunu lvalueve sağ alt noktası olan rvalue.bu alt sol ve sağ üst noktaları nelerdir yapar? Sol alt nokta, hareket etmeye izin veren klasik lvalue'nun genelleştirilmesidir. Yani bu generalized lvalue.biz adını verdik glvalue.Kısaltma hakkında tartışabilirsiniz, ama (sanırım) mantıkla değil. Ciddi kullanımda bir generalized lvalue şekilde yine de kısaltılacağını varsaydık , bu yüzden hemen yapmalıyız (veya risk karışıklığı). W'nun sağ üst noktası sağ alttan daha az geneldir (şimdi her zamanki gibi denir rvalue). Bu nokta, bir nesnenin tekrar atıfta bulunamayacağı (bir yıkıcı hariç) orijinal saf fikrini temsil eder. Bunun specialized rvalueaksine ifadeyi beğendim generalized lvalueamapure rvalueKazanmak için kısaltılmıştır prvalue(ve muhtemelen haklı olarak). Yani, W'nin sol bacağı lvalueve glvalueve sağ bacağı prvalueve rvalue.Tesadüfen, her değer bir glvalue veya bir öndeğerdir, ancak her ikisi de değildir.

Bu yapraklar üst orta W: im; yani, kimliği olan ve taşınabilen değerler. Bu ezoterik canavarlar için bizi iyi bir isme yönlendirecek hiçbir şeyimiz yok. (Taslak) standart metinle çalışan insanlar için önemlidir, ancak bir hane adı olması pek olası değildir. Bize rehberlik etmek için isimlendirme konusunda gerçek bir kısıtlama bulamadık, bu yüzden merkez, bilinmeyen, garip, sadece xpert veya hatta x-puan için 'x' seçtik.

Steve nihai ürünü gösteriyor


14
Evet, ne anlama geldiğini anlamak istiyorsanız, C ++ komitesinin orijinal önerilerini ve tartışmalarını standarttan okumak daha iyidir: D
Ivan Kush

8
Değişmezler kimliğe sahip değildir ve taşınamazlar; yine de faydalıdır.
DrPizza

Sadece bir şeyi açıklığa kavuşturmak istiyorum. int && f () {dönüş 1; } ve Sınıfım && g () {dönüş Sınıfım (); } xvalue döndür, değil mi? O zaman f () ifadelerinin kimliğini nerede bulabilirim; ve G();"? Kimlikleri var, çünkü return ifadesinde, aynı nesneye atıfta bulunan başka bir ifade var - bunu doğru anladım mı?
Dániel Sándor

6
@DrPizza Standarda göre: string değişmezleri lvalues, diğer tüm değişmez değerler prvalues'dir. Açıkçası, dize olmayan değişmezlerin taşınmaz olması gerektiğini söyleyebilirsiniz, ancak standart bu şekilde yazılmaz.
Brian Vandenberg

59

GİRİŞ

ISOC ++ 11 (resmi olarak ISO / IEC 14882: 2011), C ++ programlama dili standardının en son sürümüdür. Bazı yeni özellikler ve kavramlar içerir, örneğin:

  • rvalue referansları
  • xvalue, glvalue, prevalue ifade değeri kategorileri
  • anlambilimi taşıma

Yeni ifade değeri kategorilerinin kavramlarını anlamak istiyorsak, değer ve değer referansları olduğunu bilmemiz gerekir. Değerlerin sabit olmayan değer referanslarına geçirilebileceğini bilmek daha iyidir.

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

Çalışma taslağı N3337'den (yayınlanan ISOC ++ 11 standardına en yakın taslak) Lvalues ​​ve rvalues ​​başlıklı alt bölümden alıntı yaparsak değer kategorileri kavramlarının sezgisini kazanabiliriz.

3.10 Değerler ve değerler [basic.lval]

1 İfadeler, Şekil 1'deki sınıflandırmaya göre sınıflandırılmıştır.

  • Bir lvalue (tarihsel olarak denir, çünkü atama ifadesinin sol tarafında lvalues ​​görünebilir) bir işlevi veya nesneyi belirtir. [Örnek: E, işaretçi türünün bir ifadesiyse, * E, E'nin işaret ettiği nesneye veya işleve başvuran bir lvalue ifadesidir. Başka bir örnek olarak, dönüş türü bir değer referansı olan bir işlev çağrılmasının sonucu bir değerdir. —End örneği]
  • Bir xvalue (bir "eXpiring" değeri), genellikle kullanım ömrünün sonuna yakın olan bir nesneyi de belirtir (örneğin, kaynaklarının taşınabilmesi için). Xvalue, rvalue referanslarını içeren belirli ifade türlerinin sonucudur (8.3.2). [Örnek: Dönüş türü rvalue başvurusu olan bir işlevi çağırmanın sonucu xvalue'dur. —End örneği]
  • Bir glvalue (“genelleştirilmiş” lvalue) bir lvalue veya xvalue'dur.
  • Bir değer (tarihsel olarak, atama ifadesinin sağ tarafında görünebileceğinden, değer olarak adlandırılır) bir x değeri,
    geçici bir nesne (12.2) veya alt nesnesi veya
    bir nesne ile ilişkili olmayan bir değerdir .
  • Bir değer (“saf” değer) x değeri olmayan bir değerdir. [Örnek: Dönüş türü
    başvuru olmayan bir işlevi çağırmanın sonucu bir ön değerdir. 12, 7.3e5 veya
    true gibi bir değişmez değerin değeri de bir değerdir. —End örneği]

Her ifade, bu sınıflandırmadaki temel sınıflandırmalardan birine aittir: lvalue, xvalue veya prevalue. Bir ifadenin bu özelliğine değer kategorisi denir.

Ancak, bu alt bölümün kavramları net bir şekilde anlaması için yeterli olduğundan emin değilim, çünkü "genellikle" gerçekten genel değil, "ömrünün sonuna yakın" gerçekten somut değil, "değer referanslarını içeren" gerçekten net değil, ve "Örnek: Dönüş türü rvalue başvurusu olan bir işlevi çağırmanın sonucu xvalue'dur." Bir yılan kuyruğunu ısırmış gibi geliyor.

BİRİNCİ DEĞER KATEGORİLERİ

Her ifade tam olarak bir birincil değer kategorisine aittir. Bu değer kategorileri, değerlik, değer ve ön değer kategorileridir.

Sol taraf

E ifadesi, yalnızca E, ALREADY'nin E dışında erişilebilmesini sağlayan bir kimliğe (adres, ad veya diğer ad) sahip olduğu bir varlığı ifade ediyorsa lvalue kategorisine aittir.

#include <iostream>

int i=7;

const int& f(){
    return i;
}

int main()
{
    std::cout<<&"www"<<std::endl; // The expression "www" in this row is an lvalue expression, because string literals are arrays and every array has an address.  

    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

E ifadesi yalnızca ve yalnızca xvalue kategorisine aittir.

- dönüş türü döndürülen nesnenin türüne bir rvalue referansı olan örtülü veya açık bir şekilde bir işlevin çağrılmasının sonucu veya

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;
}

- nesne türüne bir rvalue referansına yayın veya

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;
}

- nesne ifadesinin bir xvalue olduğu, referans olmayan türde statik olmayan bir veri üyesini belirten bir sınıf üyesi erişim ifadesi veya

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;
}

- birinci işlenenin xvalue ve ikinci işlenenin veri üyesine işaretçi olduğu bir işaretçi-üye ifadesi.

Yukarıdaki kuralların etkisinin, nesnelere adlandırılmış rvalue referanslarının değer olarak kabul edildiğini ve nesnelere adsız rvalue referanslarının xvalues ​​olarak değerlendirildiğini unutmayın; işlevlere yapılan rvalue referansları, adlandırılmış olsun olmasın, lvalues ​​olarak değerlendirilir.

#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

E ifadesi yalnızca ve yalnızca E değerine veya xvalue kategorisine ait değilse, değer kategorisine aittir.

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;
}

KARIŞIK DEĞER KATEGORİLERİ

Başka iki önemli karma değer kategorisi vardır. Bu değer kategorileri rvalue ve glvalue kategorileridir.

SağDeğerler

E ifadesi yalnızca E değeri xvalue kategorisine aitse veya değer kategorisine aitse değer kategorisine aittir.

Bu tanımın, yalnızca E ifadesinin E YET dışında erişilebilir olmasını sağlayan herhangi bir kimliği olmayan bir varlığı ifade etmesi durumunda E ifadesinin değer kategorisine ait olduğu anlamına geldiğini unutmayın.

glvalues

E ifadesi yalnızca E değeri lvalue kategorisine veya xvalue kategorisine aitse glvalue kategorisine aittir.

PRATİK BİR KURAL

Scott Meyer gelmiştir yayınlanan SolDeğerler gelen SağDeğerler ayırt etmek Pratik bir çok faydalı bir kural.

  • Bir ifadenin adresini alabilirseniz, ifade bir değerdir.
  • Bir ifadenin türü bir değer değeri referansıysa (örneğin, T & veya sabit T & vb.), Bu ifade bir değerdir.
  • Aksi takdirde, ifade bir değerdir. Kavramsal olarak (ve tipik olarak da aslında), değerler işlevlerden döndürülen veya örtük tür dönüşümleri yoluyla oluşturulanlar gibi geçici nesnelere karşılık gelir. Çoğu gerçek değerler (örneğin, 10 ve 5.3) de değerlerdir.

3
Tüm değerlere örnekler ve tüm değerlere örnekler de örneklere örnektir. Düzenlediğiniz için teşekkürler!
Dániel Sándor

1
Haklısın. Üç birincil değer kategorisi yeterlidir. Değer de gerekli değildir. Ben rvalue ve glvalue kolaylık için standart olduğunu düşünüyorum.
Dániel Sándor

1
Değişkeni anlamak struct As{void f(){this;}}için zor zamanlar thisgeçirdik. Bir değer thisolması gerektiğini düşündüm . Standart 9.3.2:
r0ng

3
@ r0ng thisbir prvalue ama *thisbir lvalue olduğunu
Xeverous

1
"www" her zaman aynı adrese sahip değildir . Bir lvalue olan bir dizi olduğu için .
wally

35

C ++ 03'ün kategorileri, rvalue referanslarının ifade özniteliklerine doğru bir şekilde eklenmesini yakalamak için çok kısıtlıdır.

Bunların getirilmesiyle, adlandırılmamış bir değer referansının bir rvalue olarak değerlendirildiği, böylece aşırı yük çözünürlüğünün, kopya kurucuları üzerinde hareket yapıcılarını seçmesini sağlayacak değer referansı bağlarını tercih edeceği söylendi. Ancak bunun, örneğin Dinamik Türler ve nitelikler gibi her yerde sorunlara neden olduğu bulunmuştur .

Bunu göstermek için,

int const&& f();

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

Ön-değer taslaklarında buna izin verildi, çünkü C ++ 03'te, sınıf dışı tiplerin değerleri asla cv-nitelendirilmez. Ama amaçlanmaktadır constburada çünkü rvalue referans durumunda geçerlidir do nesneler (= bellek!) Bakın ve olmayan sınıf SağDeğerler gelen const bırakarak etrafında hiçbir nesne yoktur bu nedenle esas olan.

Dinamik türler için konu benzer niteliktedir. C ++ 03'te, sınıf türünün değerlerinin bilinen bir dinamik türü vardır - bu ifadenin statik türüdür. Çünkü başka bir şekilde elde etmek için, bir değer olarak değerlendirilen referanslara veya referanslara ihtiyacınız vardır. Adsız rvalue referansları için bu doğru değildir, ancak polimorfik davranış gösterebilirler. Yani çözmek için,

  • adsız rvalue referansları xvalue olur . Nitelikli olabilirler ve potansiyel olarak dinamik türleri farklı olabilirler. Bunlar, amaçlandığı gibi, aşırı yükleme sırasında değer referanslarını tercih ederler ve sabit olmayan değer referanslarına bağlanmazlar.

  • Daha önce bir rvalue (değişmezler, referanslar tarafından türetilmemiş türlere oluşturulan nesneler) artık bir değer haline geliyor . Aşırı yükleme sırasında xvalues ​​ile aynı tercihe sahiptirler.

  • Daha önce bir lvalue olan bir lvalue olarak kalır.

İki gruplar nitelendirilebilmektedir o yakalamak için yapılır ve farklı dinamik tipleri (olabilir glvalues ) ve aşırı yük (bağlama rvalue referans tercih burada bu SağDeğerler ).


1
cevap açıktır makul. xvalue sadece cv nitelikli ve dinamik yazılabilen bir değerdir!
ligand

26

Değer kategorilerinin cppreference.com açıklamasına rastlayana kadar uzun zamandır bununla mücadele ettim .

Aslında oldukça basit, ama genellikle ezberlenmesi zor bir şekilde açıklandığını görüyorum. Burada çok şematik olarak açıklanmıştır. Sayfanın bazı kısımlarını alıntılayacağım:

Birincil kategoriler

Birincil değer kategorileri, ifadelerin iki özelliğine karşılık gelir:

  • kimliğe sahiptir : ifadenin başka bir ifadeyle aynı varlığı ifade edip etmediğini belirlemek, örneğin nesnelerin adreslerini veya tanımladıkları işlevleri (doğrudan veya dolaylı olarak elde edilen) karşılaştırarak belirlemek mümkündür;

  • şunlardan taşınabilir : move buildtor, move atama operatörü veya move semantiği uygulayan başka bir işlev aşırı yükü ifadeye bağlanabilir.

İfadeler:

  • kimliğe sahip ve taşınamayan lvalue ifadeleri denir ;
  • kimliğe sahip olabilir ve taşınabilecek xvalue ifadeleri olarak adlandırılır ;
  • kimliğe sahip olmayanlar ve taşınabilecekleri değer ifadeleri olarak adlandırılırlar ;
  • kimliğine sahip değildir ve taşınamazlar.

lvalue

Lvalue ("sol değer") ifadesi, kimliği olan ve taşınamayan bir ifadedir .

rvalue (C ++ 11'e kadar), prevalue (C ++ 11'den beri)

Bir değer ("saf değer") ifadesi, kimliği olmayan ve taşınabilen bir ifadedir .

xValue

Xvalue ("expired value") ifadesi, kimliği olan ve taşınabilen bir ifadedir .

glvalue

Bir glvalue ("genelleştirilmiş lvalue") ifadesi, bir lvalue veya xvalue olan bir ifadedir. Bu kimliğe sahip . Hareket ettirilebilir veya taşınmayabilir.

rvalue (C ++ 11'den beri)

Bir rvalue ("right value") ifadesi, bir ön değer veya x değeri olan bir ifadedir. Buradan taşınabilir . Kimliği olabilir veya olmayabilir.


1
Bazı kitaplarda, x değerlerinin x değerinin "uzman" veya "istisnai"
ifadesinden

Ve daha da önemlisi, tüm kapsamlı örnek listeleri.
Ciro Santilli 法轮功 病毒 审查 六四 事件 法轮功

19

Bu yeni kategoriler mevcut değer ve değer kategorileriyle nasıl ilişkilidir?

Bir C ++ 03 değeri hala bir C ++ 11 değeri, C ++ 03 değeri ise C ++ 11 değeri olarak adlandırılır.


14

Stroustrup'u okuduktan ve değer / lvalue ayrımını anladığımı düşündükten sonra bile beni şaşırtan bir noktada, yukarıdaki mükemmel cevaplara bir ek. Gördüğünde

int&& a = 3,

int&&bir tür olarak okumak ve bu abir değerlik olduğu sonucuna varmak çok caziptir . Değil:

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

abir adı vardır ve ipso facto bir değerdir. Türünü bir &&parçası olarak düşünmeyin a; sadece size neyin abağlanmasına izin verildiğini söyleyen bir şey .

Bu, özellikle T&&yapıcılardaki tür argümanları için önemlidir . Yazarsan

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

_tiçine kopyalayacaksınız t. İhtiyacın var

Foo::Foo(T&& _t) : t{std::move(_t)} {}Eğer hareket etmek istiyorsan. Dışarı çıktığımda derleyicim beni uyarır mı move?


1
Bu cevabın netleştirilebileceğini düşünüyorum. "Neye abağlanmasına izin verilir?": Tabii, ancak 2. ve 3. satırda değişkenleriniz c & b'dir ve bu, ona bağlanan bir tür değildir ve burada türü aönemsizdir, değil mi? aBildirilirse satırlar aynı olur int a. Buradaki asıl fark, 1. satırda const
a'nın

12

Önceki cevaplar değer kategorilerinin arkasındaki teoriyi kapsamlı bir şekilde ele aldığından, eklemek istediğim başka bir şey daha var: aslında onunla oynayabilir ve test edebilirsiniz.

Değer kategorileri ile bazı uygulamalı denemeler için decltype belirleyicisini kullanabilirsiniz . Davranışı, üç birincil değer kategorisini (xvalue, lvalue ve prevalue) açıkça ayırır.

Önişlemciyi kullanmak bize biraz tasarruf sağlar ...

Birincil kategoriler:

#define IS_XVALUE(X) std::is_rvalue_reference<decltype((X))>::value
#define IS_LVALUE(X) std::is_lvalue_reference<decltype((X))>::value
#define IS_PRVALUE(X) !std::is_reference<decltype((X))>::value

Karışık kategoriler:

#define IS_GLVALUE(X) (IS_LVALUE(X) || IS_XVALUE(X))
#define IS_RVALUE(X) (IS_PRVALUE(X) || IS_XVALUE(X))

Şimdi değer kategorisindeki cppreference'den tüm örnekleri (neredeyse) çoğaltabiliriz .

İşte C ++ 17 ile bazı örnekler (terse static_assert için):

void doesNothing(){}
struct S
{
    int x{0};
};
int x = 1;
int y = 2;
S s;

static_assert(IS_LVALUE(x));
static_assert(IS_LVALUE(x+=y));
static_assert(IS_LVALUE("Hello world!"));
static_assert(IS_LVALUE(++x));

static_assert(IS_PRVALUE(1));
static_assert(IS_PRVALUE(x++));
static_assert(IS_PRVALUE(static_cast<double>(x)));
static_assert(IS_PRVALUE(std::string{}));
static_assert(IS_PRVALUE(throw std::exception()));
static_assert(IS_PRVALUE(doesNothing()));

static_assert(IS_XVALUE(std::move(s)));
// The next one doesn't work in gcc 8.2 but in gcc 9.1. Clang 7.0.0 and msvc 19.16 are doing fine.
static_assert(IS_XVALUE(S().x)); 

Birincil kategoriyi anladıktan sonra karışık kategoriler biraz sıkıcıdır.

Daha fazla örnek (ve deneme) için derleyici gezgininde aşağıdaki bağlantıya göz atın . Ancak meclisi okuma zahmetine girmeyin. Tüm ortak derleyicilerde çalıştığından emin olmak için birçok derleyici ekledim.


Bence #define IS_GLVALUE(X) IS_LVALUE(X) || IS_XVALUE(X)aslında gerektiğini #define IS_GLVALUE(X) (IS_LVALUE(X) || IS_XVALUE(X))aksi takdirde ne olacağını bakmak &&ikisini IS_GLVALUE.
Gabriel Devillers
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.