読者です 読者をやめる 読者になる 読者になる

C++ なんとかvalues

C++には lvalue やら rvalue やらたくさんありますが、混乱している人もいるかもしれないのでめちゃくちゃ簡単にまとめておきます。
多分間違ってるところがあると思うので、その時は教えて下さい。よろしくお願いします。

lvalue

簡単にいえば左辺にして代入できるもののことです。*1
named object (名前があるオブジェクト)と言ったほうがいいかもしれませんね。
( int& foo() { return i; } foo() = 1; とか書けますよね。)

template <typename T>
void f(T& t) { std::cout << "T&" << std::endl; } // lvalue reference
template <typename T>
void f(T&& t) { std::cout << "T&&" << std::endl; } // rvalue reference

int i=0;
int& foo() { return &i; }

int main()
{
    int xv = 0; // 0 is rvalue(prvalue)
    f(xv);      // T&    xv is lvalue
    f(foo());   // T& foo() is lvalue
}

xvalue

C++ の規格より引用します。(§5 の最初あたりですね)
以下の expression はすべて xvalue となります。

  1. 明示的か非明示的かどうかにかかわらず、戻り値にオブジェクトへの rvalue reference をもつ関数呼び出しの結果
  2. rvalue reference へのキャスト
  3. オブジェクトの reference でも static でもないデータメンバへのアクセス
  4. 1つ目のオペランドが xvalue で、2つ目のオペランドがデータメンバへのオペランドである .* pointer-to-member expression ( ->* だと一つ目のオペランドが xvalue でないのでダメ? )

pointer-to-member expression とは

class C
{
public:
    constexpr C() : m_(0) {}
    void disp() { std::cout << m_ << std::endl; }
    int m_;
};

int main()
{
    C c;
    C* pc = new C();
    int C::* pm = &C::m_;
    c.*pm = 10;
    pc->*pm = 20;
    c.disp();   // 10
    pc->disp(); // 20

    delete pc;
}

c.*pm と pc->*pm が pointer-to-member expression です。const object の mutable メンバには使えないとか色々ありますが、詳しくは手元の規格を参照してください。(§5.5 pointer-to-member expression)

一般的に,この規則によって named rvalue references *2 は lvalues として扱われ、unnamed rvalue references to objects*3は xvalues として扱われます。また、関数への rvalue references は名前があるかないかにかかわらず lvalues として扱われます。

struct A {
int m;
};
A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A&&>(a);

f(), f().m, static_cast(a), a + a という expression はすべて xvalue で、ar は lvalue となります。

prvalue(pure rvalue)

xvalue でない rvalue のことです。例えば、戻り値に参照型を持たない関数の呼出の結果は prvalue です。他にも、0 や true 等の文字列以外のリテラルも prvalue です。
prvalue のメンバ変数も prvalue だったり。
【追記 2013/05/16】ラムダ式も prvalue ですね。

template <typename T>
void f(T& t) { std::cout << "T&" << std::endl; } // lvalue reference
template <typename T>
void f(T&& t) { std::cout << "T&&" << std::endl; } // rvalue reference

int i=0;
int bar() { return 1; }

struct hoge { int m; };

int main()
{
    f(1);     // T&&:     1 is prvalue
    f(bar()); // T&&: bar() is prvalue
    f(false); // T&&: false is prvalue
    hoge().m; // prvalue
}

glvalue

lvalue または xvalue の事です。(すみませんこれ以上の説明が思い浮かびませんでした。)

rvalue

大雑把に言いますと、一時オブジェクトのことです。xvalue と prvalue をまとめたものとして考えられます。
unnamed object(名前がないオブジェクト)とも言えます。
C++11の rvalue reference とは一時オブジェクトを保持するためのものですが、

std::string str1 = "bar";
std::string&& str2 = "foo";
std::string&& str3 = str1; // Error! str1 is lvalue
std::string&& str4 = str2; // Error! str2 is lvalue

3行目は明らかにエラーですね。
4行目で str1 は名前を持ってしまっている(一時オブジェクトでない)ので rvalue ではなく lvalue となり、エラーとなります。

最後に

もっと詳しい解説は、ググるかなにかで調べてもらえれば同志の方がたくさん書かれていらっしゃいますので、そちらに丸投げさせて頂きます。あくまで簡単なまとめということでお願いします。

*1:この表現はおそらく間違っています。

*2:std::string&& str="hoge"; での str のこと

*3:X() など