右辺値、左辺値、x値、glvalues、およびprvaluesとは何ですか?


1356

C ++ 03では、式は右辺値または左辺値のいずれかです。

C ++ 11では、式は次のようになります。

  1. 右辺値
  2. 左辺値
  3. xvalue
  4. 輝かしい
  5. prvalue

2つのカテゴリが5つのカテゴリになりました。

  • これらの新しい表現のカテゴリは何ですか?
  • これらの新しいカテゴリは、既存の右辺値および左辺値のカテゴリとどのように関連していますか?
  • C ++ 0xの右辺値と左辺値のカテゴリは、C ++ 03と同じですか?
  • これらの新しいカテゴリが必要なのはなぜですか?されているWG21の神々は、ちょうど私たち単なる人間を混同しようとしていますか?

9
@フィリップ・ポッター:C ++ 03で?はい。標準の左辺値から右辺値への変換があるため、左辺値は右辺値として使用できます。
James McNellis、2010

14
@タイラー:「それに割り当てることができる場合、それは左辺値です。それ以外の場合は、右辺値です。」->間違っていますstring("hello") = string("world")。クラスの右辺値に割り当てることができます:。
fredoverflow 2010

4
これは値のカテゴリであることに注意してください。式には、さらに多くのプロパティがあります。これらには、ビットフィールド(true / false)、一時的(true / false)およびタイプ(そのタイプ)が含まれます。
ヨハネスシャウブ-litb 2010

30
上記のフレッドのリンクは、ここでのどの回答よりも優れていると思います。ただし、リンクは機能していません。次の場所に移動しました:stroustrup.com/terminology.pdf
R.マルティーニョフェルナンデス

74
C ++では、型にも型がある
nielsbot '28 / 10/28

回答:


634

このドキュメントはそれほど短くない紹介として役立つかもしれないと思います:n3055

全体の虐殺は移動の意味論から始まりました。移動できる式とコピーできない式があると、突然把握しやすい規則により、移動できる式とその方向の区別が要求されました。

ドラフトに基づいて私が推測していることから、r / l値の区別は同じままです。

それらは必要ですか?新しい機能を放棄したい場合は、おそらくそうではありません。しかし、より良い最適化を可能にするには、おそらくそれらを採用する必要があります。

n3055の引用:

  • 左辺値は、(いわゆる、歴史的に、左辺値が代入式の左側に表示されなかったため)関数またはオブジェクトを指定します。 [例:Eがポインター型の式の場合、*E が指すオブジェクトまたは関数を参照する左辺値式E です。別の例として、戻り値の型が左辺値参照である関数を呼び出した結果は左辺値です。]
  • xValue(「期限切れ」値)も(そのリソースは、例えば、移動させることができるように)通常、その寿命の終わり近くに、オブジェクトを指します。xvalueは、右辺値参照を含む特定の種類の式の結果です。 [例:戻り値の型が右辺値参照である関数を呼び出した結果はx値です。]
  • glvalue (「一般化」左辺値)は左辺値はxValue
  • 右辺値は、(いわゆる、歴史的に、右辺値が代入式の右側に表示される可能性があるため)はxValue、一時オブジェクトまたはそのサブオブジェクト、またはオブジェクトに関連付けられていない値です。
  • prvalue(「純粋な」右辺)がはxValueない右辺値です。 [例:戻り値の型が参照ではない関数を呼び出した結果はprvalueです]

問題のドキュメントは、新しい命名法の導入の結果として起こった規格の正確な変更を示しているため、この質問の優れたリファレンスです。


ありがとう、この回答は本当に役に立ちます!しかし、私のコンパイラは、xvaluesとprvaluesの例に同意しません。それらは正反対です。右辺値参照で返すとprvalueが返され、値で返すとxvalueが返されます。それらを混同してしまいましたか、それともテストベッドが壊れていますか?私はこれをGCC 4.6.1、clang(svnから)、MSVCで試しましたが、すべて同じ動作を示します。
KimGräsman、2014年

おっと、リンクをたどったところ、例がソースに含まれていることに気づきました。私は標準の私のコピーを見つけに行き、それが何を言っているかをチェックします...
キム・グラスマン14年

4
ここからのマクロを使用して、さまざまな式をテストします:stackoverflow.com/a/6114546/96963 それはそれらが物事を誤診している可能性があります。
KimGräsman14年

1
xvalueの追加は移動のセマンティクスではありません。左辺値と右辺値の両方でのみ、移動のセマンティクス、完全な前方参照、右辺値の参照はまだうまく機能します。xvalueはdecltype演算子専用であると思います。オペランド式がxvalueの場合、decltypeは右辺値参照のタイプを示します。
リガンド、

1
@MuhamedCicak「すべての式は左辺値または右辺値のいずれかです」:これは真です。そして標準(または文書n3055)はそれが偽であるとは言っていません。この文に取り消し線が引かれた理由は、ドキュメントの2つのバージョン間の変更点を見ていたことです。より正確な説明が追加された後に不要になったため、この文は削除されました。
最大

337

これらの新しい表現のカテゴリは何ですか?

FCD(n3092)は優れた記述を有します。

—左辺値(代入式の左側に左辺値が表示される可能性があるため、歴史的に呼ばれる)は、関数またはオブジェクトを指定します。[例:Eがポインター型の式の場合、* Eは、Eが指すオブジェクトまたは関数を参照する左辺値式です。別の例として、戻り値の型が左辺値参照である関数を呼び出した結果は左辺値です。—例を終了]

— xvalue(「eXpiring」値)は、通常、その寿命の終わり近くにあるオブジェクトも参照します(そのため、たとえば、リソースが移動される場合があります)。xvalueは、右辺値参照を含む特定の種類の式の結果です(8.3.2)。[例:戻り値の型が右辺値参照である関数を呼び出した結果はx値です。—例を終了]

— glvalue(「一般化された」lvalue)は、lvalueまたはxvalueです。

—右辺値(割り当て値の右側に右辺が現れる可能性があるため、歴史的に呼ばれる)は、x値、一時オブジェクト(12.2)またはそのサブオブジェクト、あるいはオブジェクトに関連付けられていない値です。

— prvalue(「純粋な」右辺値)は、x値ではない右辺値です。[例:戻り値の型が参照ではない関数を呼び出した結果は、prvalueです。12、7.3e5、trueなどのリテラルの値もprvalueです。—例を終了]

すべての式は、この分類法の基本的な分類の1つであるlvalue、xvalue、またはprvalueに属しています。式のこのプロパティは、その値カテゴリと呼ばれます。[注:条項5の各組み込み演算子の説明は、それが生成する値のカテゴリーと、それが期待するオペランドの値のカテゴリーを示しています。たとえば、組み込みの代入演算子は、左のオペランドが左辺値であり、右のオペランドがprvalueであり、結果として左辺値を生成することを期待しています。ユーザー定義の演算子は関数であり、期待される値と生成される値のカテゴリは、パラメータと戻り値の型によって決まります。-エンドノート

3.10左辺値と右辺値のセクション全体を読むことをお勧めします。

これらの新しいカテゴリは、既存の右辺値および左辺値のカテゴリとどのように関連していますか?

再び:

分類

C ++ 0xの右辺値と左辺値のカテゴリは、C ++ 03と同じですか?

右辺値のセマンティクスは、特に移動セマンティクスの導入により進化しました。

これらの新しいカテゴリが必要なのはなぜですか?

そのため、移動の構築/割り当てを定義してサポートできます。


54
ここの図が好きです。「すべての式は、この分類法の基本的な分類の1つであるlvalue、xvalue、またはprvalueに正確に属しています」で答えを始めるのが役立つと思います 次に、図を使用して、これらの3つの基本クラスを組み合わせてglvalueとrvalueを作成することを示します。
Aaron McDaid

2
「is glvalue」は「is not prvalue」と同等であり、「is rvalue」は「is not lvalue」と同等です。
Vladimir Reshetnikov

2
これは私に最も役立ちました:bajamircea.github.io/assets/2016-04-07-move-forward/…(値カテゴリのベン図)
John P

1
@AaronMcDaidこんにちは、簡単な質問あなたは/誰かが答えることができれば...なぜ名前を付けないglvalueようlvaluelvalueなどplvalue、一貫していますか?
Vijay Chavda

184

私はあなたの最後の質問から始めます:

これらの新しいカテゴリが必要なのはなぜですか?

C ++標準には、式の値カテゴリを処理する多くのルールが含まれています。一部のルールでは、左辺値と右辺値を区別しています。たとえば、過負荷の解決に関しては。他のルールは、glvalueとprvalueを区別します。たとえば、不完全型または抽象型のglvalueを持つことができますが、不完全型または抽象型のprvalueはありません。この用語を使用する前は、実際にlvalue / rvalueを参照するglvalue / prvalueを区別する必要のあるルールであり、それらは意図せず間違っていたか、ルールの説明と例外がたくさん含まれていました。右辺値参照...」したがって、glvaluesとprvaluesの概念に独自の名前を付けるのは良い考えのようです。

これらの新しい表現のカテゴリは何ですか?これらの新しいカテゴリは、既存の右辺値および左辺値のカテゴリとどのように関連していますか?

C ++ 98と互換性のある左辺値と右辺値という用語はまだ残っています。右辺値をxvaluesとprvaluesの2つのサブグループに分け、lvaluesとxvaluesをglvaluesと呼びます。Xvalueは、名前のない右辺値参照のための新しい種類の値カテゴリです。すべての式は、lvalue、xvalue、prvalueの3つのうちの1つです。ベン図は次のようになります。

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

関数の例:

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

ただし、名前付き右辺値参照は左辺値であることも忘れないでください。

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

165

これらの新しいカテゴリが必要なのはなぜですか?WG21の神々は私たちを単なる人間に混乱させようとしているだけなのでしょうか?

他の回答(多くはそうですが)がこの特定の質問に対する回答を本当に捉えているとは思いません。はい、これらのカテゴリなどは移動のセマンティクスを可能にするために存在しますが、複雑さは1つの理由で存在します。これは、C ++ 11での移動に関する1つの違反ルールです。

あなたがそうすることが疑いもなく安全であるときだけ、あなたは動くべきです。

これが、これらのカテゴリが存在する理由です。それらから移動しても安全な値について話すことができ、そうでない値について話すことができます。

r値参照の最も初期のバージョンでは、動きは簡単に起こりました。簡単すぎる。ユーザーが本当に意図していないときに、物を暗黙的に移動する可能性がたくさんあったことは簡単です。

移動しても安全な状況は次のとおりです。

  1. 一時的またはそのサブオブジェクトである場合。(適正値)
  2. ユーザーが明示的に移動するように言ったとき

これを行う場合:

SomeType &&Func() { ... }

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

これは何をしますか?古いバージョンの仕様では、5つの値が入る前に、これは動きを引き起こしました。もちろんそうです。右辺値参照をコンストラクターに渡したため、右辺値参照を受け取るコンストラクターにバインドされます。それは明白です。

これにはただ1つの問題があります。あなたはそれを動かすように頼みませんでした。ああ、あなたはそれが&&手掛かりであるはずだったと言うかもしれませんが、それがルールを破ったという事実は変わりません。valテンポラリーには名前がないため、テンポラリーではありません。一時的なものの寿命を延ばした可能性がありますが、それは一時的なものではないことを意味します。他のスタック変数と同じです。

それが一時的なものではなく、移動するように要求しなかった場合、移動は間違っています。

明白な解決策はval、左辺値を作ることです。つまり、そこから移動することはできません。いいよ; 名前が付けられているため、左辺値です。

一度それをしたら、それはどこSomeType&&でも同じことを意味するとはもはや言えません。名前付き右辺値参照と名前なし右辺値参照を区別しました。まあ、名前付き右辺値参照は左辺値です。それが上記の解決策でした。それでは、名前のない右辺値参照(上記の戻り値Func)を何と呼びますか?

左辺値から移動できないため、左辺値ではありません。そして、を返すことで移動できる必要があり&&ます。他に明示的に何かを移動するように明示的に言うことができますか std::move結局、それが戻ってきます。方程式の左側にある可能性があるため、右辺値(古いスタイル)ではありません(実際には少し複雑です。この質問と以下のコメントを参照してください)。左辺値でも右辺値でもありません。それは新しい種類のものです。

私たちが持っているのは、暗黙のうちに移動できることを除いて、左辺値として扱うことができる値です。これをxvalueと呼びます。

xvalueは、他の2つのカテゴリの値を取得するものです。

  • prvalueは、実際には前のタイプの右辺値の新しい名前にすぎません。つまり、それらはxvalueではない右辺値です。

  • 左辺値は、多くのプロパティを共有しているため、1つのグループ内のx値と左辺値を結合したものです。

つまり、実際には、すべてxvaluesと、特定の場所のみに移動を制限する必要があるということです。それらの場所は、右辺値カテゴリーによって定義されます。prvaluesは暗黙的な移動であり、xvaluesは明示的な移動です(std::movexvalueを返します)。


11
@トーマス:それは例です。戻り値がどのように作成されるかは関係ありません。重要なのは、それがを返すこと&&です。
Nicol Bolas

1
注:prvaluesは方程式の左辺に置くこともできます-次のようにX foo(); foo() = X;...この根本的な理由から、私は上記の優れた答えを最後までたどることができません。新しいxvalue、および古いスタイルのprvalue。これは、lhsにある可能性があるという事実に基づいています。
Dan Nissenbaum 2013年

1
Xクラスであること; X foo();関数宣言でありfoo() = X();、コード行である。(foo() = X();上記のコメントの2番目の括弧のセットは省略しました。)この使用法を強調表示して投稿した質問については、stackoverflow.com
。Dan Nissenbaum 2013年

1
@DanNissenbaum "xvalueを代入式の左側に置くことはできません"-なぜそうしないのですか?ideone.com/wyrxiTを
Mikhail

1
啓発的な答え。これが間違いなくここでの最良の答えです。それは私に新しい価値カテゴリーと以前に何が起こったかを紹介する根拠を与えました。
ニコス2018

136

IMHO、その意味について最良の説明は、私たち与えたStroustrup氏のアカウントの例に+テイクをダニエル・シャーンドルモハン

Stroustrup:

今、私は真剣に心配していました。明らかに私たちは行き詰まりか混乱のどちらかまたは両方に向かった。私は昼食時間を費やして分析を行い、(値の)どのプロパティが独立しているかを確認しました。2つの独立したプロパティのみがありました。

  • has identity –つまり、アドレス、ポインタ、ユーザーは2つのコピーが同一かどうかなどを判断できます。
  • can be moved from –つまり、不確定ではあるが有効な状態の「コピー」のソースに任せることができます。

これにより、正確に3種類の値があるという結論に至りました(大文字を使用して否定を示す正規表現表記のトリックを使用-急いでいました):

  • iM:IDがあり、移動できません
  • im:アイデンティティを持ち、移動できる(たとえば、左辺値を右辺値参照にキャストした結果)
  • Im:IDがなく、移動できます。

    4番目の可能性はIM、(IDがなく、移動できないC++)他の言語では(または、私が思うに)役に立ちません。

これら3つの基本的な値の分類に加えて、2つの独立したプロパティに対応する2つの明白な一般化があります。

  • i:アイデンティティがある
  • m:から移動できます

そのため、この図をボードに載せました。 ここに画像の説明を入力してください

ネーミング

名前を付ける自由は限られていることに気づきました。左側の2つのポイント(iMおよびのラベルが付いているi)は、多少形式的である人々が呼び出したものでlvaluesあり、右側の2つのポイント(mおよびのラベルが付いた Im)は、多少の形式的である人々です。と呼びましたrvalues。これは、命名に反映されているはずです。つまり、の左側の「レッグ」にW関連する名前がlvalueあり、右側の「レッグ」にW関連する名前があるはずrvalue.です。この全体の議論/問題は、右辺値参照と移動のセマンティクスの導入から生じることに注意してください。これらの概念は、単にだけからなるストレイチーの世界には存在しないrvalueslvalues。誰かがその考えが

  • すべてvalueが、lvalueまたはrvalue
  • Anがlvalueないrvaluervalueできませんlvalue

は、私たちの意識に深く埋め込まれ、非常に有用な特性を持っています。この二分法の痕跡は、規格草案全体に見られます。私たちは全員、それらのプロパティを保持する(そしてそれらを正確にする)べきであることに同意しました。これにより、命名の選択がさらに制約されました。標準ライブラリの表現が(一般化)rvalueを意味するために使用していることを確認したので、標準ライブラリmの期待とテキストを保持するには、の右下のポイントにW名前を付ける必要があります rvalue.

これにより、ネーミングに関する集中的な議論が行われました。最初に、私たちは意味するlvalue.べきか一般化すべきかを決定する必要がありましたか?Doug Gregorが率いる中心的な言葉遣いで、単語がどちらか一方を意味するように修飾された場所をリストしました。リストが作成され、ほとんどの場合、そして最もトリッキー/もろいテキストは 現在を意味します。「昔は」何も動かされなかったので、これは左辺値の古典的な意味です。は、の新しい概念です。また、のTOPLEFTポイントを命名することは、すべての値であることを私たちに財産を与えるか、その両方ではなく。lvalueiMilvaluelvalueiMmoveC++0xW lvaluelvaluervalue

だから、の左上の点がWあるlvalueと右下の点があるrvalue.ことが左下と右上のポイントを作るんか?左下の点は、古典的な左辺値の一般化であり、移動が可能です。だから、generalized lvalue.私たちはそれに名前を付けました。glvalue.あなたは、略語について誤解する ことができますが、ロジックではそうではありません。真剣に使用するgeneralized lvalue と、どういうわけかとにかく省略されると想定していたため、すぐに実行することをお勧めします(または、混乱を招く可能性があります)。Wの右上の点は、右下よりも一般的ではありません(今のところ、これまでどおりと呼ばれていますrvalue)。その点は、移動することができるオブジェクトの元の純粋な概念を表します。これは、(デストラクタを除いて)再度参照できないためです。とspecialized rvalueは対照的に私はフレーズが好きだっgeneralized lvaluepure rvalueprvalue勝つために省略された(そしておそらく正しい)。したがって、Wの左脚はlvalueglvalueあり、右脚はでprvalueあり、rvalue.ちなみに、すべての値はglvalueまたはprvalueのいずれかですが、両方ではありません。

これにより、W:の上部中央がim残ります。つまり、IDがあり、移動できる値です。私たちは本当に、これらの難解な獣の良い名前に私たちを導くものは何もありません。(ドラフト)標準テキストを扱う人々にとって重要ですが、世帯名になる可能性は低いです。命名の本当の制約を見つけるためのガイドが見つからなかったため、中心、不明、奇妙、xpertのみ、またはx定格の「x」を選択しました。

最終製品を披露するスティーブ


14
うん、彼らが何を意味するのかを理解したいのであれば、標準よりもC ++コミティのオリジナルの提案や議論を読む方が良いです:D
Ivan Kush

8
リテラルにはIDがなく、移動できません。それにもかかわらず、それらは有用です。
DrPizza

はっきりさせたいだけです。int && f(){return 1; }およびMyClass && g(){return MyClass(); } xvalueを返しますよね?次に、式f();のアイデンティティをどこで見つけることができますか。そして「g();」?returnステートメントには別の式があり、それらが参照しているのと同じオブジェクトを参照しているため、それらは同一性を持っています-私はそれを正しく理解していますか?
ダニエル・シャーンドル

6
@DrPizza標準によると、文字列リテラルはlvaluesで、他のすべてのリテラルはprvaluesです。厳密に言えば、文字列以外のリテラルは動かないようにすべきだと主張することはできますが、それは標準が書かれている方法ではありません。
ブライアンヴァンデンバーグ2017

59

前書き

ISOC ++ 11(正式にはISO / IEC 14882:2011)は、C ++プログラミング言語の標準の最新バージョンです。これには、いくつかの新機能と概念が含まれています。次に例を示します。

  • 右辺値参照
  • xvalue、glvalue、prvalue式の値のカテゴリ
  • セマンティクスを移動

新しい式の値カテゴリの概念を理解したい場合は、右辺値と左辺値の参照があることに注意する必要があります。右辺値が非const右辺値参照に渡されることを知っている方が良いです。

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

ワーキングドラフトN3337(公開されたISOC ++ 11標準に最も類似したドラフト)のLvaluesおよびrvaluesというタイトルのサブセクションを引用すると、値カテゴリの概念を直観的に理解できます。

3.10左辺値と右辺値[basic.lval]

1式は、図1の分類に従って分類されます。

  • 左辺値(割り当て式の左側に左辺値が現れる可能性があるため、これまではいわゆる)は、関数またはオブジェクトを指定します。[例:Eがポインター型の式の場合、* Eは、Eが指すオブジェクトまたは関数を参照する左辺値式です。別の例として、戻り値の型が左辺値参照である関数を呼び出した結果は左辺値です。—例を終了]
  • xvalue(「eXpiring」値)は、通常、その寿命の終わり近くにあるオブジェクトも指します(そのため、たとえば、リソースが移動される場合があります)。xvalueは、右辺値参照を含む特定の種類の式の結果です(8.3.2)。[例:戻り値の型が右辺値参照である関数を呼び出した結果はx値です。—例を終了]
  • glvalue(「一般化された」lvalue)は、lvalueまたはxvalueです。
  • 右辺値(割り当て値の右側に右辺値が現れる可能性があるため、歴史的に呼ばれる)は、x値、
    一時オブジェクト(12.2)またはそのサブオブジェクト、あるいは
    オブジェクトに関連付けられていない値です。
  • prvalue(「純粋な」右辺値)は、x値ではない右辺値です。[例:戻り値の型が
    参照ではない関数を呼び出した結果は、prvalueです。12、7.3e5、
    true などのリテラルの値もprvalueです。—例を終了]

すべての式は、この分類法の基本的な分類の1つであるlvalue、xvalue、またはprvalueに属しています。式のこのプロパティは、その値カテゴリと呼ばれます。

しかし、このサブセクションが概念を明確に理解するのに十分であるかどうかはよくわかりません。「通常」は実際には一般的ではなく、「その寿命の終わり近く」は実際には具体的ではなく、「右辺値参照の関与」はあまり明確ではないためです。および「例:戻り値の型が右辺値参照である関数を呼び出した結果はx値です。」蛇が尻尾を噛んでいるように聞こえます。

主な値のカテゴリ

すべての式は、1つの主要な値カテゴリに属しています。これらの値カテゴリは、lvalue、xvalue、およびprvalueカテゴリです。

左辺値

式Eがlvalueカテゴリに属する​​のは、Eが、ALREADYがEの外部からアクセスできるようにするID(アドレス、名前、またはエイリアス)を持っているエンティティを参照する場合のみです。

#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は、次の場合に限り、xvalueカテゴリに属します。

—暗黙的または明示的に、戻り値の型が返されるオブジェクトの型への右辺値参照である関数の呼び出しの結果、または

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

—オブジェクト型への右辺値参照へのキャスト、または

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

—オブジェクト式がxvalueである非参照型の非静的データメンバーを指定するクラスメンバーアクセス式、または

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

—最初のオペランドがxvalueで、2番目のオペランドがデータメンバーへのポインターであるメンバーへのポインター式。

上記のルールの効果は、オブジェクトへの名前付き右辺値参照は左辺値として扱われ、オブジェクトへの名前なし右辺値参照はx値として扱われることに注意してください。関数への右辺値参照は、名前が付けられているかどうかに関係なく、左辺値として扱われます。

#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がprvalueカテゴリに属する​​のは、Eがlvalueカテゴリにも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;
}

混合値のカテゴリー

さらに2つの重要な混合値カテゴリがあります。これらの値カテゴリは、右辺値と右辺値のカテゴリです。

右辺値

式Eは、Eがxvalueカテゴリまたはprvalueカテゴリに属している場合にのみ、rvalueカテゴリに属します。

この定義は、EがE YET外からアクセスできるようにするIDを持たないエンティティーを参照する場合にのみ、式Eが右辺値カテゴリーに属することを意味します。

輝かしい

式Eがglvalueカテゴリに属する​​のは、Eがlvalueカテゴリまたはxvalueカテゴリに属している場合のみです。

実践的なルール

スコット・マイヤーはしている出版さ左辺値から右辺値を区別するために親指の非常に便利なルールを。

  • 式のアドレスを取得できる場合、式は左辺値です。
  • 式のタイプが左辺値参照(たとえば、T&またはconst T&など)の場合、その式は左辺値です。
  • それ以外の場合、式は右辺値です。概念的に(そして通常は実際には)、右辺値は、関数から返されたオブジェクトや暗黙の型変換によって作成されたオブジェクトなどの一時オブジェクトに対応します。ほとんどのリテラル値(たとえば、10と5.3)も右辺値です。

3
lvaluesのすべての例とxvaluesのすべての例も、glvaluesの例です。編集ありがとうございます!
ダニエル・シャーンドル

1
あなたが正しいです。3つの主要な値のカテゴリで十分です。右辺値も必要ありません。便宜上、右辺値と右辺値は標準になっていると思います。
ダニエル・シャーンドル

1
持っていた理解に苦労変数がprvalueです。私は左辺値であるべきだと思いました。標準9.3.2が言うまで:非静的(9.3)メンバー関数の本体では、キーワードthisはprvalue式です。struct As{void f(){this;}}thisthis
r0ng 2018年

3
r0ng @ thisprvalueですが、*this左辺値である
Xeverous

1
"www" 常に同じ住所があるとは限りません配列なので左辺値です。
ウォリー

35

C ++ 03のカテゴリーは制限されているため、右辺値参照の導入を式属性に正しく取り込むことができません。

それらの導入により、名前のない右辺値参照は右辺値に評価されると言われました。そのため、オーバーロードの解決では右辺値参照バインディングが優先され、コピーコンストラクターよりも移動コンストラクターが選択されます。しかし、これは、たとえば動的型や資格など、あらゆる問題を引き起こすことがわかっています。

これを示すには、

int const&& f();

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

xvalueより前のドラフトでは、これが許可されていました。C++ 03では、非クラス型の右辺値がcv修飾されることはないためです。しかしconst、ここでオブジェクト(=メモリ!)を参照しており、非クラスの右辺値からconstを削除するのは、主にオブジェクトがないため、右辺値参照の場合に適用されることを意図しています。

動的型の問題は本質的に同じです。C ++ 03では、クラス型の右辺値には既知の動的型があります-それはその式の静的型です。別の方法で使用するには、左辺値に評価される参照または逆参照が必要です。これは、名前のない右辺値参照では当てはまりませんが、多態的な動作を示す可能性があります。それを解決するには

  • 名前のない右辺値参照はx値になります。修飾することができ、動的タイプが異なる可能性があります。それらは、意図したように、オーバーロード時に右辺値参照を優先し、非const左辺値参照にバインドしません。

  • 以前は右辺値(リテラル、非参照型へのキャストによって作成されたオブジェクト)が今はprvalueになります。オーバーロード中は、xvaluesと同じ設定になります。

  • 以前は左辺値だったものが左辺値のままです。

二つのグループは、修飾することができるものをキャプチャするために行われ、異なる動的タイプ(持つことができglvalues)と過負荷が(結合右辺値リファレンスを好む場合、それらの右辺値を)。


1
答えは明らかに合理的です。xvalueは、cv修飾および動的型付けが可能な右辺値です!
リガンド

26

値のカテゴリに関するcppreference.comの説明に出会うまで、私はこれに長い間取り組んできました。

実はシンプルなのですが、覚えにくい形で説明されていることが多いようです。ここでは非常に概略的に説明されています。ページの一部を引用します。

主なカテゴリー

主な値のカテゴリは、式の2つのプロパティに対応しています。

  • 同一性:オブジェクトのアドレスまたはオブジェクトが識別する関数(直接または間接的に取得)のアドレスを比較するなどして、式が別の式と同じエンティティを参照しているかどうかを判断することが可能です。

  • 移動させることができる器具は、意味論式に結合することができる移動する移動コンストラクタ、移動代入演算子、または他の機能過負荷:。

次の式:

  • IDを持ち、移動できないものは左辺値式と呼ばれます。
  • IDがあり、移動可能である xvalue式ます。
  • IDがなく、移動できるから呼び出されます prvalue式ます。
  • IDがないため、移動できません。

左辺値

左辺値(「左辺値」)式は、同一性持ち移動できない式です

rvalue(C ++ 11まで)、prvalue(C ++ 11以降)

prvalue(「純粋右辺値」)の式は式であるアイデンティティを持っていない移動でき

xvalue

xvalue(「期限切れの値」)式は、同一性持ちから移動できる式です。

輝かしい

glvalue(「一般化lvalue」)式は、lvalueまたはxvalueのいずれかである式です。それはアイデンティティを持っています。移動される場合とされない場合があります。

右辺値(C ++ 11以降)

右辺値(「正しい値」)式は、prvalueまたはxvalueのいずれかである式です。これは、から移動することができます。アイデンティティがある場合とない場合があります。


1
いくつかの本では、xvaluesは、そのxは、「専門家」または「例外」から来ているように示されている
noɥʇʎԀʎzɐɹƆ

そしてさらに重要なのは、それらのすべての包括的な例のリストです。
Ciro Santilli冠状病毒审查六四事件法轮功

19

これらの新しいカテゴリは、既存の右辺値および左辺値のカテゴリとどのように関連していますか?

C ++ 03右辺値はC ++ 11左辺値ですが、C ++ 03右辺値はC ++ 11ではprvalueと呼ばれます。


14

Stroustrupを読み、右辺値/左辺値の区別を理解したと思った後でも混乱した点で、上記の優れた回答の1つの補遺。あなたが見るとき

int&& a = 3

int&&型として読み取り、それaが右辺値であると結論付けるのは非常に魅力的です。そうではありません:

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

a名前があり、lpvalueです。&&をのタイプの一部として考えないでくださいa。それaは何にバインドすることが許可されているかを伝えるものにすぎません。

これは、特にT&&コンストラクタの型引数の場合に重要です。あなたが書くなら

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

にコピー_ttます。あなたは必要です

Foo::Foo(T&& _t) : t{std::move(_t)} {}移動したい場合。私が省略したとき、コンパイラは警告を発しましたかmove


1
この答えは明確になると思います。「aバインドが許可されているもの」:確かに、2行目と3行目では変数はcとbであり、バインド先はaではなく、のタイプaはここでは関係ありませんよね。aが宣言されてint aいる場合、行は同じになります。ここでは、実際の主な違いは、ライン1にする必要がないことであるconst3にバインドするために
フェリックスDombek

12

以前の回答では値のカテゴリの背後にある理論を網羅していたため、追加したいことがもう1つあります。実際に遊んでテストすることができます。

値のカテゴリを実際に試す場合は、decltype指定子を使用できます。その動作は、3つの主要な値カテゴリ(xvalue、lvalue、およびprvalue)を明示的に区別します。

プリプロセッサを使用すると、入力の手間が省けます...

主なカテゴリー:

#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

混合カテゴリー:

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

これで、値カテゴリのcppreferenceから(ほぼ)すべての例を再現できます

C ++ 17の例をいくつか示します(静的static_assertの場合)。

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

混合カテゴリーは、主要カテゴリーを理解すると、つまらないものになります。

その他の例(および実験)については、コンパイラエクスプローラーの次のリンクを確認してください。ただし、アセンブリを読んでわざわざしないでください。すべての一般的なコンパイラで機能することを確認するために、多くのコンパイラを追加しました。


さもなければあなたが二人だったらどうなるかを#define IS_GLVALUE(X) IS_LVALUE(X) || IS_XVALUE(X)実際に#define IS_GLVALUE(X) (IS_LVALUE(X) || IS_XVALUE(X))見てみるべきだと思います。&&IS_GLVALUE
Gabriel Devillers
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.