左辺値と右辺値
右辺値と左辺値
C++では式の型(T, const T, volatile T, T&, T&&)以外にもうひとつ式の値を分類する用語がある。いままで左辺値、右辺値といっていたような類いである。
int x; x = 3;
いままではxが=の左側にあるからxは左辺値、3は右側にあるから右辺値といったような分類をされていたが、いまのC++では少し拡張された分類となっている。ここでいうxはlvalueであり、3はprvalueというものに相当する。
これらの分類は大きく分けて2種類あり、lvalueとrvalueに分類される。〔もちろんlとrの由来は左と右である。〕
lvalue
lvalueとは、変数名がつけられたオブジェクトやポインタが指し示すオブジェクトである。
変数
int x; int* p = &x;
スタック上にあるint型オブジェクトxはlvalueである。また、xのポインタであるpも、変数名が付けられたオブジェクトであり、lvalueである。更にpの指し示すオブジェクトも(要するにxだが)lvalueである。
参照
int x; // lvalue int& lr = x; // lvalue int&& rr = 0; // lvalue
これらx, lr, rrはいずれも変数名が付けられたオブジェクトだからlvlalueである。
lvalueのメンバ変数
struct S { int x; };
クラスSのメンバ変数xはlvalueである。同様に左辺値参照、右辺値参照〔ここでの左辺値・右辺値は本トピックとは無関係である〕であってもlvalueである。
メンバーへのポインタの実態
struct S { }; int S::* x; S s; s.*x; // lvalue static_cast<S&&>(s).*x; // lvalue
lvalueでもrvalueでもメンバへのポインタを経由してアクセスしたものはlvalueである。
左辺値参照を返す関数
int& func(int& x) { return x; } int x; func(x) = 3;
このように左辺値参照を返す関数はlvalueである。
xvalue
xvalueは、いつ消滅してもおかしくないオブジェクトである。由来はeXpiring valueから。従ってxvalueなオブジェクトは今後使われることを想定しなくても良い。これはmove semanticsに利用される。
右辺値参照を返す関数
int&& func() { return 1; } func(); // xvalue
右辺値参照を返す関数はxvalueである。std::move()は右辺値参照を返すのでxvalueである。
右辺値参照へのキャスト
int x; static_cast<int&&>(x); // xvalue
std::move()のしていることは右辺値参照へのキャストである。
xvalueの非参照メンバ変数
参照型のメンバ変数は上述の通りlvalueである。一方でxvalueの非参照メンバ変数はxvalueである。
struct S { int x; }; S s; static_cast<S&&>(s).x; // xvalue
prvalue
簡単に言うとlvalueやxvalueに分類されないその他である。
prvalueの非参照メンバ変数
xvalueと同様にprvalueのメンバ変数もprvalueである。
関数の戻り値
int f(); // prvalue
関数の戻り値は、戻り値が左辺値参照或いは右辺値参照でなければprvalueである。
右辺値参照以外へのキャスト
static_cast<int>x; // prvalue
その他キャストはprvalueである。
その他演算子等の結果
1 + 2; // prvalue new int(10); // int*型のprvalue
glvalueとrvalue
最後に、lvalue及びxvalueを総称してglvalueという。また、prvalue及びxvalueを総称してrvalueという。xvalueはどちらにも属することが分かる。