Namespaces
Variants

Value categories

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Value categories
Order of evaluation
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

各C++ (演算子とその被演算子、リテラル、変数名など)は、2つの独立した特性によって特徴付けられます: 値カテゴリ 。各式は何らかの非参照型を持ち、各式は3つの主要な値カテゴリのうち正確に1つに属します: prvalue xvalue 、および lvalue

  • glvalue (「一般化された」左辺値)は、その評価によってオブジェクトまたは関数の同一性が決定される式である;
  • prvalue (「純粋な」右辺値)は、その評価が
  • 組み込み演算子のオペランドの値を計算する(このようなprvalueは 結果オブジェクト を持たない)、または
  • オブジェクトを初期化する(このようなprvalueは 結果オブジェクト を持つと言われる)。
結果オブジェクトは、変数、 new-expression によって作成されたオブジェクト、 temporary materialization によって作成された一時オブジェクト、またはそれらのメンバーであり得る。非 void discarded 式は結果オブジェクト(具体化された一時オブジェクト)を持つことに注意。また、すべてのクラスおよび配列prvalueは、 decltype のオペランドである場合を除き、結果オブジェクトを持つ。
  • xvalue (「寿命切れ」値)は、リソースを再利用可能なオブジェクトを表すglvalueである;
  • lvalue はxvalueではないglvalueである;
拡張コンテンツ

歴史的に、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
}
(注:原文が既に英語のC++専門用語(rvalue, prvalue, xvalue)を使用しており、翻訳指示に従いこれらは翻訳対象外です。またHTMLタグ・属性も保持されています)
拡張コンテンツ

歴史的に、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 です:

拡張コンテンツ
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()` は左辺値、戻り値型が左辺値参照である関数呼び出し
}
拡張コンテンツ
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
}
template <int& v>
void set()
{
    v = 5; // テンプレートパラメータは左辺値
}
int a{3}; // 静的変数、コンパイル時に固定アドレスが既知
void foo()
{
    set<a>();
}
  • 戻り値の型が関数への右辺値参照である関数呼び出し、またはオーバーロードされた演算子式;
  • 関数型への右辺値参照へのキャスト式、例えば static_cast < void ( && ) ( int ) > ( x ) .
(C++11以降)

プロパティ:

  • glvalue と同じ(下記参照)。
  • lvalueのアドレスは組み込みのアドレス取得演算子で取得可能: & ++ i [1] および & std:: hex は有効な式である。
  • 変更可能なlvalueは、組み込みの代入演算子および複合代入演算子の左辺オペランドとして使用可能。
  • lvalueは lvalue参照の初期化 に使用可能。これにより式で識別されるオブジェクトに新しい名前が関連付けられる。

prvalue

以下の式は prvalue式 です:

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式 です:

  • オブジェクトへの右辺値参照を返す関数呼び出しまたはオーバーロードされた演算子式、例えば std :: move ( x ) ;
  • a [ n ] 、組み込みの 添字演算子 式で、一方のオペランドが配列右辺値であるもの;
  • オブジェクト型への右辺値参照へのキャスト式、例えば static_cast < char && > ( x ) ;
(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左辺値参照の初期化 に使用でき、この場合右辺値によって識別される一時オブジェクトの寿命は参照のスコープが終了するまで 延長 されます。
  • 右辺値は 右辺値参照の初期化 に使用でき、この場合右辺値によって識別される一時オブジェクトの寿命は参照のスコープが終了するまで 延長 される。
  • 関数引数として使用される場合、および関数の 二つのオーバーロード が利用可能な場合(一方が右辺値参照パラメータを取り、もう一方がconstへの左辺値参照パラメータを取る)、右辺値は右辺値参照オーバーロードにバインドされる(したがって、コピーコンストラクタとムーブコンストラクタの両方が利用可能な場合、右辺値引数は move constructor を呼び出し、同様にコピー代入演算子とムーブ代入演算子についても同様である)。
(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が行われないことに注意してください。

脚注

  1. i が組み込み型を持つか、前置インクリメント演算子が オーバーロード されて左辺値参照を返すと仮定。
  2. 2.0 2.1 2.2 2.3 特殊な右辺値カテゴリ。詳細は 保留中のメンバ関数呼び出し を参照。
  3. i が組み込み型を持つか、後置インクリメント演算子が左辺値参照を返すように オーバーロード されていないと仮定。
  4. 「Cコミュニティ内での意見の相違はlvalueの意味を中心に展開し、一方のグループはlvalueをあらゆる種類のオブジェクトロケーターと見なしたが、もう一方のグループはlvalueが代入演算子の左側で意味を持つと主張した。C89委員会はlvalueの定義をオブジェクトロケーターとして採用した。」 -- ANSI C Rationale, 6.3.2.1/10。
  5. 「新」値カテゴリ用語 (Bjarne Stroustrup著, 2010年)。
  6. 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 値カテゴリ
注意点: - HTMLタグと属性は翻訳せず保持 - C++専門用語("value categories")は翻訳せず保持 - その他のテキストのみ日本語に翻訳

外部リンク

1. C++ value categories and decltype demystified — David Mazières, 2021
2. Empirically determine value category of expression — StackOverflow