何が lvalue で何が rvalue なのか

こういう記事も必要かもしれないと思いました.内容はタイトルそのままです.

はじめに

はじめに,と書きましたがこの項は飛ばしても問題ないです.
まず "lvalue" とか "rvalue" とかいうものが何についての概念なのかということですが,これらは C++ の "式" の持つ属性 "value category" の値です."式" には他に "型" という属性があります.

int i = 0;

i; // 式 i の型は int,value category は lvalue
0; // 式 0 の型は int,value category は rvalue

型の決め方は難しくないと思うので (たぶん),この記事では value category の決め方を書きます.以降,"i は int の lvalue である" といった言い方をします.

Lvalue,rvalue のイメージ

いきなりですが引用です.

誤解を恐れずにいえば、lvalueとは、明示的に実体のある、名前付きのオブジェクトであり、rvalueとは、一時的に生成される無名のオブジェクトである。

http://cpplover.blogspot.com/2009/11/rvalue-reference_23.html
int i = 0;

i;        // 式 i は lvalue
0;        // 式 0 は rvalue

この説明が一番分かりやすいです.まずこのイメージを何となくつかみます.

Lvalue になるもの

では lvalue になる主なものを挙げていきます.

変数
int i = 0;
i;        // i は int の lvalue

int &ri = i;
ri;       // ri は int の lvalue

int &&rri = 0;
rri;      // rri は int の lvalue

式 ri の型を int & でなく int と書いていますが,一般に式の型を考えるときに参照は考えませんので*1,そういう書き方になっています.
注意すべきなのは 3 例目です.変数 rri の型が int && であることに惑わされてはいけません.変数名を書いた式は,lvalue なのです.

Lvalue のメンバ変数
struct A { int i; };

A a = {0};
a;        // a は A の lvalue
a.i;      // a.i は int の lvalue

Lvalue はメンバ変数に伝播します.

ポインタを * したもの
int *pi = &i;
*pi;      // int の lvalue (i と同じオブジェクトを指している)

アドレスを取れているなら,その指している先は "明示的な実体" と考えられます.
ではちょっと応用です.先程の,lvalue のメンバ変数は lvalue,という話と組み合わせてみましょう.

A *pa = &a;
pa->i;    // int の lvalue

pa->i は (*pa).i と等価ですが,(*pa) が lvalue なので,pa->i == (*pa).i も lvalue になります.

Lvalue reference を返す関数の呼び出しやそれに準ずるもの
int &rf();
rf();     // int の lvalue

static_cast<A &>(a); // A の lvalue

A b;
a = b;    // A の lvalue

Lvalue reference というのは文字通り lvalue への参照なので,lvalue reference を返してきたら lvalue と思っていいわけです.

文字列リテラル
"Hello, world!"; // char const [14] の lvalue

リテラルの中では例外的です.

Prvalue になるもの (rvalue その 1)

新しい用語 prvalue を出してしまいましたが,rvalue は xvalue と prvalue に分けられ,その一つです.これは昔ながらの rvalue で,"一時的に生成される無名のオブジェクト" というイメージそのままです.

基本
int();    // int の prvalue
A{0};     // A の prvalue
Prvalue のメンバ変数
A().i;    // int の prvalue

Rvalue も,基本的にはメンバ変数に伝播します.

参照以外を返す関数の呼び出しとそれに準ずるもの
int f();
f();      // int の prvalue

static_cast<double>(i); // double の prvalue

2 + 3;    // int の prvalue

new int;  // int * の prvalue

多くの関数呼び出しや組み込み演算子呼び出しがここに属します.

文字列以外のリテラル
0;        // int の prvalue

3.14;     // double の prvalue

nullptr;  // std::nullptr_t の prvalue

this やラムダ式も同様です.

Xvalue (rvalue その 2)

前述の通り rvalue の一種です.

Xvalue のメンバ変数

これまでと同様です.

Rvalue reference を返す関数の呼び出しとそれに準ずるもの
int &&rrf();

rrf();    // int の xvalue

static_cast<A &&>(a); // A の xvalue

これも rvalue への参照を返しているから rvalue だろう,ということですね.
ここに std::move の呼び出しが含まれることは重要です.

namespace std {

template <class T>
typename remove_reference<T>::type &&move(T &&t) noexcept
{
    return static_cast<typename remove_reference<T>::type &&>(t);
}

}

std::move(a); // A の xvalue

std::move の格好は仰々しいですが,戻り値の型が rvalue reference になっている点に注目しましょう.std::move はオブジェクトを rvalue に変える関数である,ということが理解できると思います.

おわりに

とりあえず思いつくところを挙げてみました.他に重要なものがあれば教えてください.

*1:5/5