LoginSignup
7
4

More than 5 years have passed since last update.

右辺値は型ではない

Last updated at Posted at 2014-08-05

C++ の右辺値について混乱していたので、右辺値と右辺値参照についてメモしておく。

value category

C++ の式は、型とは別に value category というものを持つ。value category は prvalue, xvalue, lvalue のいずれかであり、prvalue と xvalue をまとめて 右辺値 と呼ぶ。(prvalue, xvalue, lvalue については後述)

注意すべき点として、

  • 同じ型を持つ式でも、異なる value category を持つことがある。
  • 同じオブジェクトを返す式でも、異なる value category を持つことがある。

例えば、

string s = string("hello ") + string("world");
strint t = s;

というコードにおいて、1行目の string("hello ") + string("world") の型と、2行目の右辺の s の型は同一(string 型)であるが、value category は異なっている (それぞれ prvalue と lvalue)。

ひどい例として、以下のような場合、

string&& r = string("hello");
string t = r;

r の型は string への右辺値参照だが、2行目の r という式の value category は lvalue になる。

左辺値の式から無理やり右辺値を作りたい場合は std::move 関数を使ってキャストする。

string s("hello");
string&& r = move(s);    // move(s) は xvalue (右辺値の一種)

関数のオーバーロードと右辺値

string hoge = "piyo";
string x = ???;

という式を考える。

??? の部分が hoge のような左辺値だった場合、string のコピーコンストラクタが呼び出される。一方、??? の部分が move(hoge) のような右辺値の式だった場合、string のムーブコンストラクタが呼び出される。

一般に、ある関数 f が「左辺値参照を取るバージョン」と「右辺値参照を取るバージョン」の2通りにオーバーロードされていて、かつ f の引数が右辺値の式になっている場合、右辺値参照を取るバージョンの f が呼び出される。コピーコンストラクタ vs ムーブコンストラクタの例は、このルールのスペシャルケースになっている。

prvalue, xvalue, lvalue について

prvalue

prvalue (pure rvalue) は一時オブジェクトを生じさせる式。

例:

  • 文字列以外のリテラル (100 とか nullptr とか)
  • 戻り値の型が参照型でない関数呼び出し式
  • 組み込みの算術式、論理式、比較式など

xvalue

xvalue (eXpiring value) は move 元のオブジェクトを表す式。

例:

  • 戻り値の型が右辺値参照である関数呼び出し
  • 右辺値参照へのキャスト式
  • xvalue である式の non-static クラスメンバへのアクセス式

lvalue

lvalue は一時的でないオブジェクトを表す式。

例:

  • 変数名
  • 戻り値の方が左辺値参照である関数呼び出し
  • 代入式
  • 文字列リテラル
7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4