名前空間
変種
操作

値カテゴリ

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
値カテゴリ
評価順序
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

各C++ (オペランドを持つ演算子、リテラル、変数名など) は、値カテゴリ の2つの独立したプロパティによって特徴付けられます。各式には非参照型があり、各式は3つの主要な値カテゴリのいずれか1つに正確に属します:prvaluexvalue、および lvalue

  • a glvalue (“generalized” lvalue) は、評価によってオブジェクトまたは関数のアイデンティティが決定される式です。
  • a prvalue (“pure” rvalue) は、評価によって
  • 組み込み演算子のオペランドの値を計算する (このようなprvalueは結果オブジェクトを持たない)、または
  • オブジェクトを初期化する (このようなprvalueは結果オブジェクトを持つと言われる)。
結果オブジェクトは、変数、new-式によって作成されたオブジェクト、一時的な実体化によって作成された一時オブジェクト、またはそれらのメンバである可能性があります。非void破棄された式は結果オブジェクトを持つことに注意してください(実体化された一時オブジェクト)。また、すべてのクラスおよび配列のprvalueは、decltypeのオペランドである場合を除き、結果オブジェクトを持ちます。
  • an xvalue (an “eXpiring” value) は、そのリソースを再利用できるオブジェクトを示すglvalueです。
  • an 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
}
  • 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式です

拡張コンテンツ
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 << 1str1 = 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
}
拡張コンテンツ
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; // template parameter is lvalue
}
 
int a{3}; // static variable, fixed address is known at compile-time
 
void foo()
{
    set<a>();
}
  • 関数へのrvalue参照を返す関数呼び出しまたはオーバーロードされた演算子式。
  • 関数型へのrvalue参照へのキャスト式、例えば static_cast<void(&&)(int)>(x)
(C++11以降)

プロパティ

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

[編集] prvalue

次の式はprvalue式です

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

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

  • return文のオペランドとして
  • co_return文のオペランドとして (C++20以降)
  • throw式のオペランドとして (C++17以降)

式がムーブ可能である場合、オーバーロード解決の目的で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はもはやムーブされないことに注意してください。

[編集] 脚注

  1. iが組み込み型であるか、または前置インクリメント演算子がlvalue参照を返すようにオーバーロードされていると仮定します。
  2. 2.0 2.1 2.2 2.3 特殊なrvalueカテゴリ、保留中のメンバ関数呼び出しを参照。
  3. iが組み込み型であるか、または後置インクリメント演算子がlvalue参照を返すようにオーバーロードされていないと仮定します。
  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 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
English 日本語 中文(简体) 中文(繁體)