web-dev-qa-db-ja.com

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

C++ 03では、式は rvalue または lvalue のいずれかです。

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

  1. 右辺値
  2. 左辺値
  3. xvalue
  4. glvalue
  5. prvalue

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

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

私はこの文書がそれほど短くない紹介として役立つかもしれないと思います: n3055

全体の虐殺は移動の意味論から始まった。移動することができてコピーできない表現ができたら、規則をつかむのが突然容易になるため、移動できる表現とその方向を区別する必要がありました。

私がドラフトに基づいて推測していることから、r/l値の区別は変わらず、物事を動かすという意味では乱雑になります。

彼らは必要ですか?おそらく私たちが新機能を失いたいのであれば、そうではないでしょう。しかし、より良い最適化を可能にするためには、おそらくそれらを受け入れるべきです。

引用符付け n3055

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

それが新しい命名法の導入の結果として起こった規格の正確な変化を示すので、問題の文書はこの質問のための素晴らしい参考文献です。

580

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

FCD(n3092) には、優れた説明があります。

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

- xvalue( "eXpiring"値)も、通常は存続期間の終わり近く(たとえば、リソースが移動される可能性があるように)にあるオブジェクトを指します。 xvalueは右辺値参照を含むある種の式の結果です(8.3.2)。 [例:戻り値の型が右辺値参照である関数を呼び出した結果はxvalueです。 - 終了例]

- glvalue(「一般化」左辺値)は、左辺値またはx値です。

- 右辺値(代数式の右辺に右辺値が現れる可能性があるため、いわゆる歴史的に)は、xvalue、一時オブジェクト(12.2)、またはそれらのサブオブジェクト、あるいはオブジェクトに関連付けられていない値です。

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

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

3.10左辺値と右辺値 を読んでください。

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

再び:

Taxonomy

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

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

なぜこれらの新しいカテゴリが必要なのでしょうか。

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

322
dirkgently

最後の質問から始めましょう。

なぜこれらの新しいカテゴリが必要なのでしょうか。

C++標準には、式の値のカテゴリを扱う多くの規則が含まれています。左辺値と右辺値を区別する規則がいくつかあります。例えば、それが過負荷解決になると。他の規則はglvalueとprvalueを区別します。たとえば、不完全型または抽象型のglvalueを持つことはできますが、不完全型または抽象型のprvalueはありません。この用語が使われる前は、glvalue/prvalueを実際に区別する必要があるルールはlvalue/rvalueを参照していました。右辺値参照... "ですから、glvaluesとprvaluesの概念にそれら自身の名前を単に与えることは良い考えのように思えます。

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

C++ 98と互換性のある、左辺値と右辺値という用語がまだあります。右辺値を2つのサブグループ、xvaluesとprvaluesに分割したところで、lvaluesとxvaluesをglvaluesと呼びます。 X値は、名前のない右辺値参照のための新しい種類の値カテゴリです。すべての式は、次の3つのうちのいずれかです。l値、x値、pr値。ベン図は次のようになります。

    ______ ______
   /      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
}
171
sellibitze

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

私は他の答え(それらの多くはそうですが良い)が本当にこの特定の質問に対する答えをとらえているとは思わない。はい、これらのカテゴリなどは移動セマンティクスを許可するために存在しますが、複雑さは1つの理由で存在します。これはC++ 11でものを動かすことの1つの不可抗力な規則です:

間違いなく安全な場合にのみ移動します。

それがこれらのカテゴリーが存在する理由です:それらから移動するのが安全であるところで値について話すことができる、そしてそうでないところで値について話すことができるようにするため。

初期のバージョンのr値参照では、動きは簡単に起こりました。 Too簡単。ユーザーが本当に意図していなかったときに暗黙のうちに物事を動かす可能性がたくさんあったことは簡単に十分です。

これは、何かを動かしても安全な状況です。

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

あなたがこれをするならば:

SomeType &&Func() { ... }

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

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

これにはただ一つ問題があります。あなたはそれを動かすためにaskをしませんでした。おお、あなたは&&が手がかりであるべきだったと言うかもしれません、しかしそれはそれが規則を破ったという事実を変えません。一時変数には名前がないため、valは一時的なものではありません。あなたはテンポラリーの寿命を延ばしたかもしれませんが、それはテンポラリーではないことを意味します。他のスタック変数とまったく同じです。

それが一時的なものではなく、あなたがそれを動かすことを求めなかったならば、動くことは間違っています

明らかな解決策はvalを左辺値にすることです。これはあなたがそこから動くことができないことを意味します。いいよ;それは名前が付けられているので、それは左辺値です。

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

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

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

Xvaluesは他の2つの値のカテゴリーを得るためのものです。

  • Prvalueは、実際には前のタイプの右辺値の新しい名前です。つまり、それらはare not xvaluesという右辺値です。

  • Glvaluesはxvalueとlvalueを1つのグループに結合したものです。なぜなら、それらは共通の多くの特性を共有しているからです。

それで、本当に、それはすべてx値と動きを正確にそして特定の場所だけに制限する必要性に帰着します。それらの場所は右辺値カテゴリによって定義されます。 prvaluesは暗黙的な移動で、xvaluesは明示的な移動です(std::moveはxvalueを返します)。

150
Nicol Bolas

私見、その意味についての最良の説明は私たちに与えた Stroustrup + DánielSándorMohan の例を考慮に入れる:

Stroustrup:

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

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

これにより、正確に3種類の値があるという結論に至りました(ネガティブを示すために大文字を使用する正規表現のトリックを使用して、急いでいた)。

  • iM:IDがあり、移動できません
  • im:IDがあり、移動可能です(たとえば、左辺値を右辺値参照にキャストした結果)
  • Im:IDがないため、移動できます。

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

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

  • i:IDを持っています
  • m:から移動可能

これにより、この図をボードに配置することになりました。 enter image description here

ネーミング

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

  • すべてのvalueは、lvalueまたはrvalueのいずれかです。
  • lvaluervalueではなく、rvaluelvalueではありません

は私たちの意識に深く埋め込まれており、非常に有用な特性であり、この二分法の痕跡はドラフト標準全体に見られます。私たちは皆、それらのプロパティを保存する(そして正確にする)ことに同意しました。これにより、命名の選択肢がさらに制限されました。標準ライブラリの文言はrvalueを使用してm(一般化)を意味するため、標準ライブラリの期待とテキストを保持するためにWの右下の点を観察しました。 rvalue.という名前にする必要があります

これにより、ネーミングの議論が集中しました。まず、lvalue.を決定する必要がありましたlvalueiMを意味するか、一般化iを意味しますか?ダグ・グレゴール率いる、コア言語の言葉遣いで、単語lvalueがどちらかを意味するように修飾された場所をリストしました。リストが作成され、ほとんどの場合、最も扱いにくい/壊れやすいテキストでは、lvalueは現在iMを意味します。 「昔は」何も動かされなかったため、これは左辺値の古典的な意味です。 moveC++0xの斬新な概念です。また、Wlvalueの左上のポイントに名前を付けると、すべての値がlvalueまたはrvalueであるが、両方ではないというプロパティが得られます。

それで、Wの左上の点はlvalueであり、右下の点はrvalue.です。それは左下と右上の点を何にするのでしょうか。左下の点は、古典的な左辺値の一般化であり、移動が可能です。 generalized lvalue.という名前です。glvalue.という名前を付けました。略語については口論することはできますが、論理的にはそうではありません。深刻な使用では、generalized lvalueはとにかく省略されると想定したので、すぐに実行する(または混乱を招く)方がよいと考えました。 Wの右上のポイントは、右下よりも一般的ではありません(これまでどおり、rvalueと呼ばれます)。その点は、(デストラクタによる場合を除いて)再び参照することができないため、移動できるオブジェクトの元の純粋な概念を表します。 specialized rvalueとは対照的にgeneralized lvalueというフレーズが好きでしたが、pure rvalueと省略されたprvalueが勝ちました(そしておそらくそうでしょう)。したがって、Wの左脚はlvalueおよびglvalueであり、右脚はprvalueおよびrvalue.です。ちなみに、すべての値はglvalueまたはprvalueですが、両方。

これにより、Wの上部中央が残ります:im;つまり、IDがあり、移動できる値。私たちはこれらの難解な獣の良い名前に私たちを導くものを本当に持っていません。それらは(ドラフト)標準テキストで作業する人々にとって重要ですが、一般的な名前にはなりそうにありません。ネーミングに実際の制約はありませんでした。そのため、センター、不明、奇妙、xpertのみ、またはx-ratedに「x」を選択しました。

Steve showing off the final product

123
Ivan Kush

紹介

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

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

新しい式の値カテゴリの概念を理解したい場合は、右辺値と左辺値の参照があることに注意する必要があります。右辺値を非定数右辺値参照に渡すことができることを知っておくとよいでしょう。

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

作業ドラフトN3337(公開されたISOC++ 11標準に最も類似したドラフト)から左辺値と右辺値というタイトルのサブセクションを引用すると、値カテゴリの概念のいくつかの直観を得ることができます。

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

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

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

すべての式は、この分類法の基本的な分類(lvalue、xvalue、またはprvalue)のいずれかに属します。式のこのプロパティは、値カテゴリと呼ばれます。

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

プライマリ値のカテゴリ

すべての式は、厳密に1つのプライマリ値カテゴリに属します。これらの値カテゴリは、左辺値、x値、およびpr値のカテゴリです。

左辺値

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

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

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

#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は、Eが左辺値にもxvalueカテゴリにも属さない場合にのみprvalueカテゴリに属します。

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カテゴリに属する​​場合にのみ、右辺値カテゴリに属します。

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

glvalues

式Eは、Eが左辺値カテゴリまたはxvalueカテゴリに属する​​場合にのみglvalueカテゴリに属します。

実際的なルール

Scott Meyerには、右辺値と左辺値を区別する非常に便利な経験則 published があります。

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

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

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

これを示すために、考えてみましょう

int const&& f();

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

Xvalue以前のドラフトでは、これが許可されていました。C++ 03では、クラス以外の型の右辺値がcv修飾されていないためです。しかし、ここではdoがオブジェクト(= memory!)を参照しているため、constが右辺値参照の場合に適用されることを意図しています。周りのオブジェクト。

動的型の問題も同様です。 C++ 03では、クラス型の右辺値は既知の動的型を持ちます - それはその式の静的型です。それを別の方法で持つためには、左辺値に評価される参照または間接参照が必要です。名前のない右辺値参照ではそうではありませんが、多相的な振る舞いを示すことがあります。それを解決するために、

  • 無名の右辺値参照は xvalues になります。それらは修飾することができ、潜在的にそれらの動的タイプを異ならせることができます。意図したように、それらはオーバーロード時に右辺値参照を優先し、非定数左辺値参照にバインドしません。

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

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

そして、2つのグループ化が行われ、修飾可能で異なる動的タイプを持つことができるもの( glvalues )と、オーバーロードが右辺値参照バインディングを好むもの( rvalues )を取ります。

値のカテゴリ についてのcppreference.comの説明に出会うまで、私は長い間これに苦労してきました。

それは実際にはかなり簡単です、しかし私はそれが暗記するのが難しい方法で説明されることが多いことがわかります。ここでそれは非常に概略的に説明されています。ページの一部を引用します。

主なカテゴリー

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

  • 同一性を持つ :式が他の式と同じ実体を参照しているかどうかを判断することができます。

  • から移動できます。移動コンストラクタ、移動代入演算子、または移動セマンティクスを実装する別の関数オーバーロードを式にバインドできます。

式は、

  • 左辺値式 ;と同一性があり、移動できません。
  • xvalue式 ;と同一性があり、そこから移動することができます。
  • prvalue式 ;というアイデンティティを持たず、移動することができます。
  • アイデンティティを持たず、そこから移動できないものは使用されません。

左辺値

左辺値( "left value")式は、 が同一性 を持ち、から移動できないという式です。

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

Prvalue( "pure rvalue")式は、 が同一性 を持たず、から移動できる式です。

x値

Xvalue( "expiring value")式は、 が同一性 を持ち、から移動できる式です。

大値

Glvalue( "一般化左辺値")式は、左辺値またはx値のいずれかである表現です。それは アイデンティティ を持ちます。移動してもしなくてもかまいません。

右辺値(C++ 11以降)

右辺値( "正しい値")式は、prvalueまたはxvalueのいずれかである式です。から移動できます。それはアイデンティティを持っていてもいなくてもよいです。

24
Felix Dombek

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

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

15
fredoverflow

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は名前を持ち、ipso factoは左辺値です。 &&aの型の一部と考えないでください。それはaがどんなものに束縛されることが許されるかをあなたに言っているだけのものです。

これは特にコンストラクタ内のT&&型引数にとって重要です。書くなら

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

_ttにコピーします。あなたが必要

移動したい場合はFoo::Foo(T&& _t) : t{std::move(_t)} {}moveを省略したときに、私のコンパイラが私に警告してくれたでしょうか。

14
Mohan

これまでの回答で値カテゴリーの背後にある理論が徹底的に網羅されていたので、追加したいことがもう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(trunk). Clang 7.0.0 and msvc 19.16 are doing fine.
static_assert(IS_XVALUE(S().x)); 

あなたが一次カテゴリーを考え出したら、混合カテゴリーは一種の退屈です。

いくつかの例(および実験)については、次の コンパイラエクスプローラのリンク を参照してください。しかし、議会を読むことを邪魔しないでください。私はそれがすべての一般的なコンパイラに渡って確実に機能するように、たくさんのコンパイラを追加しました。

2
thebrandre