Value categories
各C++ 式 (演算子とその被演算子、リテラル、変数名など)は、2つの独立した特性によって特徴付けられます: 型 と 値カテゴリ 。各式は何らかの非参照型を持ち、各式は3つの主要な値カテゴリのうち正確に1つに属します: prvalue 、 xvalue 、および lvalue 。
-
- 組み込み演算子のオペランドの値を計算する(このようなprvalueは 結果オブジェクト を持たない)、または
- オブジェクトを初期化する(このようなprvalueは 結果オブジェクト を持つと言われる)。
-
結果オブジェクトは、変数、
new-expression
によって作成されたオブジェクト、
temporary materialization
によって作成された一時オブジェクト、またはそれらのメンバーであり得る。非
void
discarded
式は結果オブジェクト(具体化された一時オブジェクト)を持つことに注意。また、すべてのクラスおよび配列prvalueは、
decltypeのオペランドである場合を除き、結果オブジェクトを持つ。
| 拡張コンテンツ |
|---|
|
歴史的に、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 is a prvalue or an 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++標準改訂で大幅な変更が行われています。詳細は以下の History を参照してください。
| 拡張コンテンツ |
|---|
|
これらの用語は名前とは裏腹に、値ではなく式を分類するものです。
このコードを実行
#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 expressions です:
- 変数名、関数名 、 テンプレートパラメータオブジェクト (C++20以降) 、またはデータメンバの名前(型に関わらず)。例えば std:: cin や std:: hex など。変数の型が右辺値参照であっても、その名前からなる式は左辺値式である(ただし 移動可能な式 を参照のこと)。
| 拡張コンテンツ |
|---|
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 } |
- 戻り値の型が左辺値参照である関数呼び出しまたはオーバーロードされた演算子式。例えば 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()` は左辺値、戻り値型が左辺値参照である関数呼び出し } |
- a = b 、 a + = b 、 a % = b およびその他のすべての組み込み 代入演算子と複合代入演算子 の式;
- ++ a および -- a 、組み込みの 前置インクリメントおよび前置デクリメント の式;
- * p 、組み込みの 間接参照 の式;
- a [ n ] および p [ n ] 、組み込みの 添字演算子 の式 (ただし a [ n ] の一方のオペランドが配列左辺値である場合 (C++11以降) ;
-
a.
m
、
オブジェクトメンバーアクセス
の式(ただし
mがメンバー列挙子または非静的メンバー関数の場合、または a が右辺値かつ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
が左辺値で
mpがデータメンバへのポインタである場合); -
p
-
>
*
mp
、組み込みの
ポインタへのメンバポインタアクセス
式(
mpがデータメンバへのポインタである場合); - a, b 、組み込みの コンマ演算子 式( b が左辺値である場合);
- a ? b : c 、特定の b と c に対する 三項条件演算子 式(例:両方が同じ型の左辺値である場合など、詳細は 定義 を参照);
- 文字列リテラル (例: "Hello, world!" );
- 左辺値参照型へのキャスト式(例: static_cast < int & > ( x ) または static_cast < void ( & ) ( int ) > ( x ) );
- 左辺値参照型の定数 テンプレートパラメータ ;
template <int& v> void set() { v = 5; // テンプレートパラメータは左辺値 } int a{3}; // 静的変数、コンパイル時に固定アドレスが既知 void foo() { set<a>(); }
|
(C++11以降) |
プロパティ:
- glvalue と同じ(下記参照)。
- lvalueのアドレスは組み込みのアドレス取得演算子で取得可能: & ++ i [1] および & std:: hex は有効な式である。
- 変更可能な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() { // lvalueではない、`v`はスカラー型intのテンプレートパラメータ const int* a = &v; // ill-formed v = 3; // ill-formed: 代入の左辺値としてlvalueが必要 }
|
(C++11以降) |
|
(C++20以降) |
プロパティ:
- rvalue と同じ(下記参照)。
- prvalueは ポリモーフィック になりえない:それが表すオブジェクトの 動的型 は常に式の型である。
- 非クラス非配列のprvalueは cv修飾 できません (cv修飾された型への 参照に束縛 されるために 実体化 される場合を除く) (C++17以降) 。(注:関数呼び出しやキャスト式は非クラスcv修飾型のprvalueをもたらすことがあるが、cv修飾子は一般に直ちに除去される。)
-
prvalueは
不完全型
を持つことができない(型
void
の場合(後述参照)、または
decltype指定子で使用される場合を除く)。 - prvalueは 抽象クラス型 またはその配列型を持つことができない。
xvalue
以下の式は xvalue式 です:
-
a.
m
、
オブジェクトのメンバー
式。ここで
a
は右辺値、
mはオブジェクト型の非静的データメンバー; -
a.
*
mp
、
オブジェクトへのメンバーポインタ
式。ここで
a
は右辺値、
mpはデータメンバーへのポインタ; - a, b 、組み込みの コンマ演算子 。ここで b はxvalue;
- a ? b : c 、特定の b と c に対する 三項条件演算子 (詳細は 定義 を参照);
|
(C++11以降) |
|
(C++17以降) |
|
(C++23以降) |
プロパティ:
- rvalue(下記)と同じです。
- glvalue(下記)と同じです。
特に、すべての右辺値と同様に、xvalueは右辺値参照に束縛され、すべての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 {}; // C++23標準からの例: 7.2.1 値カテゴリ [basic.lval] struct A { int m; }; A&& operator+(A, A); A&& f(); int main() { A a; A&& ar = static_cast<A&&>(a); // 戻り値型が右辺値参照の関数呼び出しはxvalue static_assert(is_xvalue<decltype( (f()) )>::value); // オブジェクト式のメンバ、オブジェクトがxvalue、`m`は非静的データメンバ static_assert(is_xvalue<decltype( (f().m) )>::value); // 右辺値参照へのキャスト式 static_assert(is_xvalue<decltype( (static_cast<A&&>(a)) )>::value); // 戻り値型がオブジェクトへの右辺値参照の演算子式 static_assert(is_xvalue<decltype( (a + a) )>::value); // 式`ar`はlvalue、`&ar`は有効 static_assert(is_lvalue<decltype( (ar) )>::value); [[maybe_unused]] A* ap = &ar; } |
混合カテゴリ
glvalue
A glvalue expression はlvalueまたはxvalueのいずれかです。
プロパティ:
- glvalueは、lvalue-to-rvalue、array-to-pointer、またはfunction-to-pointerの 暗黙変換 によってprvalueに暗黙的に変換されることがあります。
- glvalueは ポリモーフィック である可能性があります:それが識別するオブジェクトの 動的型 は、必ずしも式の静的型とは限りません。
- glvalueは、式で許可されている場合、 不完全型 を持つことができます。
rvalue
rvalue式 は、prvalueまたはxvalueのいずれかです。
プロパティ:
- 右辺値のアドレスは組み込みのアドレス取得演算子では取得できません: & int ( ) 、 & i ++ [3] 、 & 42 および & std :: move ( x ) は無効です。
- 右辺値は組み込みの代入演算子または複合代入演算子の左辺オペランドとして使用できません。
- 右辺値は const左辺値参照の初期化 に使用でき、この場合右辺値によって識別される一時オブジェクトの寿命は参照のスコープが終了するまで 延長 されます。
|
(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式には result object が存在しない。 |
(C++17以降) |
ビットフィールド
ビットフィールドを指定する式 bit-field (例えば a. m 、ここで a は型 struct A { int m : 3 ; } の左辺値)はglvalue式です:これは代入演算子の左辺オペランドとして使用できますが、そのアドレスを取得することはできず、非const左辺値参照をそれにバインドすることはできません。const左辺値参照または右辺値参照はビットフィールドのglvalueから初期化できますが、ビットフィールドの一時コピーが作成されます:直接ビットフィールドにバインドされることはありません。
ムーブ適格式任意の変数の名前からなる式はlvalue式であるが、そのような式は以下のオペランドとして現れた場合、ムーブ適格となる可能性がある 式がムーブ適格である場合、それは オーバーロード解決 の目的で rvalueまたはlvalueのいずれかとして (C++23まで) rvalueとして (C++23以降) 扱われる(したがって ムーブコンストラクタ が選択される可能性がある)。詳細は ローカル変数とパラメータからの自動ムーブ を参照。 |
(C++11以降) |
歴史
CPL
プログラミング言語 CPL は、式の値カテゴリを導入した最初の言語でした:すべてのCPL式は「右辺モード」で評価できますが、「左辺モード」で意味を持つのは特定の種類の式のみです。右辺モードで評価される場合、式は値(右辺値、または rvalue )の計算規則と見なされます。左辺モードで評価される場合、式は実質的にアドレス(左辺値、または lvalue )を提供します。ここでの「左」と「右」は、「代入の左側」と「代入の右側」を意味していました。
C
Cプログラミング言語も同様の分類体系に従いましたが、代入の役割はもはや重要ではなくなりました:Cの式は「左辺値式」とその他(関数と非オブジェクト値)の間に分類され、「左辺値」とはオブジェクトを識別する式、すなわち「位置指定子値」を意味します [4] 。
C++98
C++11以前のC++はCのモデルに従っていましたが、非左辺値式に対して「右辺値」という名称を復活させ、関数を左辺値に変更し、参照は左辺値にバインドできるが、constへの参照のみが右辺値にバインドできるという規則を追加しました。C言語では非左辺値であったいくつかの式が、C++では左辺値式になりました。
C++11
C++11でムーブセマンティクスが導入されたことにより、値カテゴリは式の2つの独立した特性を特徴付けるために再定義されました [5] :
- 同一性を持つ : 式が別の式と同じ実体を参照しているかどうかを(オブジェクトのアドレスや関数の識別子を直接または間接的に比較するなどして)判定可能であること;
- 移動可能 : move constructor 、 move assignment operator 、または移動セマンティクスを実装する他の関数オーバーロードが式に束縛可能であること。
C++11では、以下のような式:
- アイデンティティを持ち、移動できない式は lvalue 式と呼ばれる;
- アイデンティティを持ち、移動できる式は xvalue 式と呼ばれる;
- アイデンティティを持たず、移動できる式は prvalue ("pure rvalue")式と呼ばれる;
- アイデンティティを持たず、移動できない式は使用されない [6] .
同一性を持つ式は「glvalue式」(glvalueは「generalized lvalue」を意味する)と呼ばれます。lvalueとxvalueの両方がglvalue式です。
移動可能な式は「右辺値式」と呼ばれます。prvalueとxvalueの両方が右辺値式です。
C++17
C++17では、 copy elision が一部の状況で必須となり、これによりprvalue式をそれらによって初期化される一時オブジェクトから分離する必要が生じ、現在のシステムが確立されました。C++11の方式とは対照的に、prvalueからはもはやmoveが行われないことに注意してください。
脚注
- ↑ i が組み込み型を持つか、前置インクリメント演算子が オーバーロード されて左辺値参照を返すと仮定。
- ↑ 2.0 2.1 2.2 2.3 特殊な右辺値カテゴリ。詳細は 保留中のメンバ関数呼び出し を参照。
- ↑ i が組み込み型を持つか、後置インクリメント演算子が左辺値参照を返すように オーバーロード されていないと仮定。
- ↑ 「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標準 (ISO/IEC 14882:2020):
-
- 7.2.1 値カテゴリ [basic.lval]
- C++17標準 (ISO/IEC 14882:2017):
-
- 6.10 左値と右値 [basic.lval]
- C++14標準 (ISO/IEC 14882:2014):
-
- 3.10 左辺値と右辺値 [basic.lval]
- C++11標準 (ISO/IEC 14882:2011):
-
- 3.10 左辺値と右辺値 [basic.lval]
- C++98標準 (ISO/IEC 14882:1998):
-
- 3.10 左辺値と右辺値 [basic.lval]
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用バージョン | 公開時の仕様 | 修正後の仕様 |
|---|---|---|---|
| CWG 616 | C++11 |
右辺値のメンバアクセスおよびメンバポインタ経由の
メンバアクセスの結果がprvalueとなっていた |
xvalueに再分類 |
| CWG 1059 | C++11 | 配列prvalueにCV修飾を適用できなかった | 許可されるよう変更 |
| CWG 1213 | C++11 | 配列右辺値の添字演算結果がlvalueとなっていた | xvalueに再分類 |
関連項目
|
C documentation
for
value categories
|
|
C documentation
for
value categories
|
|
Cドキュメント
for
値カテゴリ
|
外部リンク
| 1. | C++ value categories and decltype demystified — David Mazières, 2021 | |
| 2. |
Empirically determine value category of expression
— StackOverflow
|