何が 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 と思っていいわけです.
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
多くの関数呼び出しや組み込み演算子呼び出しがここに属します.
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