値カテゴリ
各C++ 式 (オペランドを持つ演算子、リテラル、変数名など) は、型 と 値カテゴリ の2つの独立したプロパティによって特徴付けられます。各式には非参照型があり、各式は3つの主要な値カテゴリのいずれか1つに正確に属します:prvalue、xvalue、および lvalue。
- a glvalue (“generalized” lvalue) は、評価によってオブジェクトまたは関数のアイデンティティが決定される式です。
- a prvalue (“pure” rvalue) は、評価によって
- 組み込み演算子のオペランドの値を計算する (このようなprvalueは結果オブジェクトを持たない)、または
- オブジェクトを初期化する (このようなprvalueは結果オブジェクトを持つと言われる)。
| 拡張コンテンツ |
|---|
|
歴史的には、lvalueが代入式の左辺に現れることができたため、このように呼ばれています。一般的には、常にそうであるとは限りません。 void foo(); void baz() { int a; // Expression `a` is lvalue a = 4; // OK, could appear on the left-hand side of an assignment expression int &b{a}; // Expression `b` is lvalue b = 5; // OK, could appear on the left-hand side of an assignment expression const int &c{a}; // Expression `c` is lvalue c = 6; // ill-formed, assignment of read-only reference // Expression `foo` is lvalue // address may be taken by built-in address-of operator void (*p)() = &foo; foo = baz; // ill-formed, assignment of function } |
- an rvalue はprvalueまたはxvalueです。
| 拡張コンテンツ |
|---|
|
歴史的には、rvalueが代入式の右辺に現れることができたため、このように呼ばれています。一般的には、常にそうであるとは限りません。 このコードを実行 #include <iostream> struct S { S() : m{42} {} S(int a) : m{a} {} int m; }; int main() { S s; // Expression `S{}` is prvalue // May appear on the right-hand side of an assignment expression s = S{}; std::cout << s.m << '\n'; // Expression `S{}` is prvalue // Can be used on the left-hand side too std::cout << (S{} = S{7}).m << '\n'; } 出力 42 7 |
注: この分類は過去のC++標準改訂で大幅に変更されています。詳細は以下の歴史を参照してください。
| 拡張コンテンツ |
|---|
|
これらの用語は、その名前とは異なり、値ではなく式を分類します。 このコードを実行 #include <type_traits> #include <utility> template <class T> struct is_prvalue : std::true_type {}; template <class T> struct is_prvalue<T&> : std::false_type {}; template <class T> struct is_prvalue<T&&> : std::false_type {}; template <class T> struct is_lvalue : std::false_type {}; template <class T> struct is_lvalue<T&> : std::true_type {}; template <class T> struct is_lvalue<T&&> : std::false_type {}; template <class T> struct is_xvalue : std::false_type {}; template <class T> struct is_xvalue<T&> : std::false_type {}; template <class T> struct is_xvalue<T&&> : std::true_type {}; int main() { int a{42}; int& b{a}; int&& r{std::move(a)}; // Expression `42` is prvalue static_assert(is_prvalue<decltype((42))>::value); // Expression `a` is lvalue static_assert(is_lvalue<decltype((a))>::value); // Expression `b` is lvalue static_assert(is_lvalue<decltype((b))>::value); // Expression `std::move(a)` is xvalue static_assert(is_xvalue<decltype((std::move(a)))>::value); // Type of variable `r` is rvalue reference static_assert(std::is_rvalue_reference<decltype(r)>::value); // Type of variable `b` is lvalue reference static_assert(std::is_lvalue_reference<decltype(b)>::value); // Expression `r` is lvalue static_assert(is_lvalue<decltype((r))>::value); } |
目次 |
[編集] 主要カテゴリ
[編集] lvalue
次の式はlvalue式です
- 変数、関数、テンプレートパラメータオブジェクト(C++20以降)、またはデータメンバの名前(型に関わらず)、例えば std::cin や std::endl。変数の型がrvalue参照であっても、その名前からなる式はlvalue式です(ただし、ムーブ可能式を参照)。
| 拡張コンテンツ |
|---|
void foo() {} void baz() { // `foo` is lvalue // address may be taken by built-in address-of operator void (*p)() = &foo; } struct foo {}; template <foo a> void baz() { const foo* obj = &a; // `a` is an lvalue, template parameter object } |
- lvalue参照を返す関数呼び出しまたはオーバーロードされた演算子式、例えば std::getline(std::cin, str)、std::cout << 1、str1 = str2、または ++it。
| 拡張コンテンツ |
|---|
int& a_ref() { static int a{3}; return a; } void foo() { a_ref() = 5; // `a_ref()` is lvalue, function call whose return type is lvalue reference } |
- a = b、a += b、a %= b、およびその他すべての組み込み代入および複合代入式。
- ++a および --a、組み込みの前置インクリメントおよび前置デクリメント式。
- *p、組み込みの間接参照式。
- a[n] および p[n]、組み込みの添字式、ここで a[n] の一方のオペランドが配列lvalueである場合(C++11以降)。
- a.m、オブジェクトのメンバ式。ただし、
mがメンバ列挙子または非静的メンバ関数である場合、またはaがrvalueでmがオブジェクト型の非静的データメンバである場合を除く。
| 拡張コンテンツ |
|---|
struct foo { enum bar { m // member enumerator }; }; void baz() { foo a; a.m = 42; // ill-formed, lvalue required as left operand of assignment } struct foo { void m() {} // non-static member function }; void baz() { foo a; // `a.m` is a prvalue, hence the address cannot be taken by built-in // address-of operator void (foo::*p1)() = &a.m; // ill-formed void (foo::*p2)() = &foo::m; // OK: pointer to member function } struct foo { static void m() {} // static member function }; void baz() { foo a; void (*p1)() = &a.m; // `a.m` is an lvalue void (*p2)() = &foo::m; // the same } |
- p->m、組み込みのポインタのメンバ式。ただし、
mがメンバ列挙子または非静的メンバ関数である場合を除く。 - a.*mp、オブジェクトのメンバへのポインタ式。ここでaはlvalueで
mpはデータメンバへのポインタである。 - p->*mp、組み込みのポインタのメンバへのポインタ式。ここで
mpはデータメンバへのポインタである。 - a, b、組み込みのコンマ式。ここでbはlvalue。
- a ? b : c、特定のbとcに対する三項条件式(例:両方が同じ型のlvalueである場合など、詳細については定義を参照)。
- 文字列リテラル、例えば "Hello, world!"。
- lvalue参照型へのキャスト式、例えば static_cast<int&>(x) または static_cast<void(&)(int)>(x)。
- lvalue参照型の非型テンプレートパラメータ。
template <int& v> void set() { v = 5; // template parameter is lvalue } int a{3}; // static variable, fixed address is known at compile-time void foo() { set<a>(); }
|
(C++11以降) |
プロパティ
- glvalue (以下) と同じ。
- lvalueのアドレスは組み込みのアドレス演算子で取得できます。&++i[1] および &std::endl は有効な式です。
- 変更可能なlvalueは、組み込みの代入演算子および複合代入演算子の左辺オペランドとして使用できます。
- lvalueはlvalue参照を初期化するために使用できます。これにより、式によって識別されるオブジェクトに新しい名前が関連付けられます。
[編集] prvalue
次の式はprvalue式です
- リテラル(文字列リテラルを除く)、例えば 42、true または nullptr。
- 非参照型を返す関数呼び出しまたはオーバーロードされた演算子式、例えば str.substr(1, 2)、str1 + str2、または it++。
- a++ および a--、組み込みの後置インクリメントおよび後置デクリメント式。
- a + b、a % b、a & b、a << b、およびその他すべての組み込み算術式。
- a && b、a || b、!a、組み込みの論理式。
- a < b、a == b、a >= b、およびその他すべての組み込み比較式。
- &a、組み込みのアドレス演算子式。
- a.m、オブジェクトのメンバ式。ここで
mはメンバ列挙子または非静的メンバ関数[2]。 - p->m、組み込みのポインタのメンバ式。ここで
mはメンバ列挙子または非静的メンバ関数[2]。 - a.*mp、オブジェクトのメンバへのポインタ式。ここで
mpはメンバ関数へのポインタ[2]。 - p->*mp、組み込みのポインタのメンバへのポインタ式。ここで
mpはメンバ関数へのポインタ[2]。 - a, b、組み込みのコンマ式。ここでbはprvalue。
- a ? b : c、特定のbとcに対する三項条件式(詳細については定義を参照)。
- 非参照型へのキャスト式、例えば static_cast<double>(x)、std::string{}、または (int)42。
thisポインタ。- 列挙子。
- スカラ型の非型テンプレートパラメータ。
template <int v> void foo() { // not an lvalue, `v` is a template parameter of scalar type int const int* a = &v; // ill-formed v = 3; // ill-formed: lvalue required as left operand of assignment }
|
(C++11以降) |
|
(C++20以降) |
プロパティ
- rvalue (以下) と同じ。
- prvalueはポリモーフィックになることはできません。それが示すオブジェクトの動的型は、常に式の型です。
- 非クラスの非配列prvalueはcv修飾できません、ただしcv修飾型への参照にバインドするために実体化される場合を除きます(C++17以降)。(注:関数呼び出しやキャスト式は、非クラスのcv修飾型のprvalueを返す可能性がありますが、cv修飾子は通常すぐに取り除かれます。)
- prvalueは不完全型を持つことはできません(型voidの場合、以下を参照、または
decltype指定子で使用される場合を除く)。 - prvalueは抽象クラス型またはその配列を持つことはできません。
[編集] xvalue
次の式はxvalue式です
- a.m、オブジェクトのメンバ式。ここでaはrvalueで
mはオブジェクト型の非静的データメンバである。 - a.*mp、オブジェクトのメンバへのポインタ式。ここでaはrvalueで
mpはデータメンバへのポインタである。 - a, b、組み込みのコンマ式。ここでbはxvalue。
- a ? b : c、特定のbとcに対する三項条件式(詳細については定義を参照)。
|
(C++11以降) |
|
(C++17以降) |
| (C++23から) |
プロパティ
- rvalue (以下) と同じ。
- glvalue (以下) と同じ。
特に、すべてのrvalueと同様に、xvalueはrvalue参照にバインドし、すべてのglvalueと同様に、xvalueはポリモーフィックであり、非クラスのxvalueはcv修飾されることがあります。
| 拡張コンテンツ |
|---|
|
このコードを実行 #include <type_traits> template <class T> struct is_prvalue : std::true_type {}; template <class T> struct is_prvalue<T&> : std::false_type {}; template <class T> struct is_prvalue<T&&> : std::false_type {}; template <class T> struct is_lvalue : std::false_type {}; template <class T> struct is_lvalue<T&> : std::true_type {}; template <class T> struct is_lvalue<T&&> : std::false_type {}; template <class T> struct is_xvalue : std::false_type {}; template <class T> struct is_xvalue<T&> : std::false_type {}; template <class T> struct is_xvalue<T&&> : std::true_type {}; // Example from C++23 standard: 7.2.1 Value category [basic.lval] struct A { int m; }; A&& operator+(A, A); A&& f(); int main() { A a; A&& ar = static_cast<A&&>(a); // Function call with return type rvalue reference is xvalue static_assert(is_xvalue<decltype( (f()) )>::value); // Member of object expression, object is xvalue, `m` is a non-static data member static_assert(is_xvalue<decltype( (f().m) )>::value); // A cast expression to rvalue reference static_assert(is_xvalue<decltype( (static_cast<A&&>(a)) )>::value); // Operator expression, whose return type is rvalue reference to object static_assert(is_xvalue<decltype( (a + a) )>::value); // Expression `ar` is lvalue, `&ar` is valid static_assert(is_lvalue<decltype( (ar) )>::value); [[maybe_unused]] A* ap = &ar; } |
[編集] 混合カテゴリ
[編集] glvalue
glvalue式はlvalueまたはxvalueのいずれかです。
プロパティ
- glvalueは、lvalue-to-rvalue、array-to-pointer、またはfunction-to-pointerの暗黙の型変換によってprvalueに暗黙的に変換されることがあります。
- glvalueはポリモーフィックであることがあります。それが識別するオブジェクトの動的型は、必ずしも式の静的型であるとは限りません。
- glvalueは、式で許可されている場合、不完全型を持つことができます。
[編集] rvalue
rvalue式はprvalueまたはxvalueのいずれかです。
プロパティ
- rvalueのアドレスは、組み込みのアドレス演算子では取得できません。&int()、&i++[3]、&42、および &std::move(x) は無効です。
- rvalueは、組み込みの代入演算子または複合代入演算子の左辺オペランドとして使用できません。
- rvalueはconst lvalue参照を初期化するために使用できます。この場合、rvalueによって識別される一時オブジェクトの寿命は、参照のスコープが終了するまで延長されます。
|
(C++11以降) |
[編集] 特殊カテゴリ
[編集] 保留中のメンバ関数呼び出し
a.mf および p->mf の式(ここで mf は非静的メンバ関数)と、a.*pmf および p->*pmf の式(ここで pmf はメンバ関数へのポインタ)はprvalue式として分類されますが、参照の初期化、関数引数、または関数呼び出し演算子の左辺引数として以外には使用できません。例えば (p->*pmf)(args)。
[編集] Void式
voidを返す関数呼び出し式、voidへのキャスト式、およびthrow式はprvalue式として分類されますが、参照を初期化したり、関数引数として使用したりすることはできません。これらは破棄される値の文脈(例:それ自体の一行、コンマ演算子の左辺オペランドなど)や、voidを返す関数のreturn文で使用できます。さらに、throw式は条件演算子 ?:の第2および第3オペランドとして使用できます。
|
void式は結果オブジェクトを持ちません。 |
(C++17以降) |
[編集] ビットフィールド
ビットフィールドを指定する式 (例: a.m、ここで a は型 struct A { int m: 3; } のlvalue) はglvalue式です。代入演算子の左辺オペランドとして使用できますが、そのアドレスは取得できず、非const lvalue参照をそれにバインドすることはできません。const lvalue参照またはrvalue参照はビットフィールドglvalueから初期化できますが、ビットフィールドの一時コピーが作成されます。ビットフィールドに直接バインドすることはありません。
ムーブ可能式変数名からなる式はlvalue式ですが、次の場合にはムーブ可能と見なされることがあります。 式がムーブ可能である場合、オーバーロード解決の目的でrvalueまたはlvalueとして(C++23まで)rvalueとして(C++23以降)扱われます(したがって、ムーブコンストラクタが選択される可能性があります)。詳細については、ローカル変数とパラメータからの自動ムーブを参照してください。 |
(C++11以降) |
[編集] 歴史
[編集] CPL
プログラミング言語CPLは、式に対する値カテゴリを最初に導入しました。すべてのCPL式は「右辺モード」で評価できますが、特定の種類の式のみが「左辺モード」で意味を持ちます。右辺モードで評価される場合、式は値(右辺値、またはrvalue)を計算するための規則と見なされます。左辺モードで評価される場合、式は実質的にアドレス(左辺値、またはlvalue)を与えます。「左辺」と「右辺」は、「代入の左辺」と「代入の右辺」を表していました。
[編集] C
C言語は同様の分類法に従いましたが、代入の役割はもはや重要ではありませんでした。Cの式は「lvalue式」とその他(関数や非オブジェクト値)に分類され、「lvalue」とはオブジェクトを識別する式、つまり「ロケータ値」を意味します[4]。
[編集] C++98
2011年以前のC++はCのモデルに従っていましたが、非lvalue式に「rvalue」という名前を復元し、関数をlvalueにし、参照はlvalueにバインドできるが、constへの参照のみがrvalueにバインドできるというルールを追加しました。いくつかの非lvalue C式はC++でlvalue式になりました。
[編集] C++11
C++11でムーブセマンティクスが導入されたことにより、値カテゴリは式の2つの独立した特性を特徴付けるように再定義されました[5]
- アイデンティティを持つ:オブジェクトや関数が識別するアドレス(直接的または間接的に取得される)を比較するなどして、その式が別の式と同じエンティティを参照しているかどうかを判断できること。
- ムーブ可能である:ムーブコンストラクタ、ムーブ代入演算子、またはムーブセマンティクスを実装する別の関数オーバーロードがその式にバインドできること。
C++11では、次の式が
- アイデンティティを持ち、ムーブできないものはlvalue式と呼ばれます。
- アイデンティティを持ち、ムーブできるものはxvalue式と呼ばれます。
- アイデンティティを持たず、ムーブできるものはprvalue("純粋なrvalue")式と呼ばれます。
- アイデンティティを持たず、ムーブできないものは使用されません[6]。
アイデンティティを持つ式は「glvalue式」(glvalueは「generalized lvalue」の略)と呼ばれます。lvalueとxvalueの両方がglvalue式です。
ムーブ可能な式は「rvalue式」と呼ばれます。prvalueとxvalueの両方がrvalue式です。
[編集] C++17
C++17では、一部の状況でコピーエリジョンが必須となり、これによりprvalue式とそれらによって初期化される一時オブジェクトを分離する必要が生じ、今日のようなシステムになりました。C++11のスキームとは異なり、prvalueはもはやムーブされないことに注意してください。
[編集] 脚注
- ↑ iが組み込み型であるか、または前置インクリメント演算子がlvalue参照を返すようにオーバーロードされていると仮定します。
- ↑ 2.0 2.1 2.2 2.3 特殊なrvalueカテゴリ、保留中のメンバ関数呼び出しを参照。
- ↑ iが組み込み型であるか、または後置インクリメント演算子がlvalue参照を返すようにオーバーロードされていないと仮定します。
- ↑ 「Cコミュニティ内では、lvalueの意味について意見の相違があった。あるグループはlvalueをあらゆる種類のオブジェクトロケータと見なしていたが、別のグループはlvalueが代入演算子の左辺で意味を持つと考えていた。C89委員会はlvalueの定義をオブジェクトロケータとして採用した。」 -- ANSI C Rationale, 6.3.2.1/10。
- ↑ Bjarne Stroustrupによる「新しい」値の用語、2010年。
- ↑ const prvalue(クラス型のみ許可)およびconst xvalueは
T&&オーバーロードにはバインドしませんが、const T&&オーバーロードにはバインドします。これらも標準によって「ムーブコンストラクタ」および「ムーブ代入演算子」として分類されており、この分類の目的のための「ムーブ可能である」という定義を満たしています。しかし、このようなオーバーロードは引数を変更できず、実際には使用されません。これらがない場合、const prvalueおよびconst xvalueはconst T&オーバーロードにバインドします。
[編集] 参照
- C++23標準 (ISO/IEC 14882:2024)
- 7.2.1 値カテゴリ [basic.lval]
- C++20 standard (ISO/IEC 14882:2020)
- 7.2.1 値カテゴリ [basic.lval]
- C++17 standard (ISO/IEC 14882:2017)
- 6.10 Lvalueとrvalue [basic.lval]
- C++14 standard (ISO/IEC 14882:2014)
- 3.10 Lvalueとrvalue [basic.lval]
- C++11 standard (ISO/IEC 14882:2011)
- 3.10 Lvalueとrvalue [basic.lval]
- C++98 標準 (ISO/IEC 14882:1998)
- 3.10 Lvalueとrvalue [basic.lval]
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 616 | C++11 | rvalueのメンバアクセスと メンバへのポインタによるアクセスがprvalueになった |
xvalueに再分類された |
| CWG 1059 | C++11 | 配列prvalueをcv修飾できない | 許可 |
| CWG 1213 | C++11 | 配列rvalueの添字付けがlvalueになった | xvalueに再分類された |
[編集] 関連項目
| Cドキュメント 値カテゴリ
|
[編集] 外部リンク
| 1. | C++ value categories and decltype demystified — David Mazières, 2021 | |
| 2. | Empirically determine value category of expression — StackOverflow |