「C++の基礎 - 右辺値と左辺値」の版間の差分
(ページの作成:「== 概要 == C言語では、代入演算子=の左側にあるものを左辺値(lvalue)、右側(rvalue)にあるものを右辺値として決められている。<br>…」) |
(→右辺値) |
||
30行目: | 30行目: | ||
(x + 1) = 9; // コンパイルエラー | (x + 1) = 9; // コンパイルエラー | ||
} | } | ||
</syntaxhighlight> | |||
<br><br> | <br><br> | ||
2021年3月12日 (金) 06:44時点における版
概要
C言語では、代入演算子=の左側にあるものを左辺値(lvalue)、右側(rvalue)にあるものを右辺値として決められている。
C++では、左辺値は名前を持つオブジェクト(&演算子でアドレスを取得できる)、右辺値は名前を持たない一時オブジェクトのことである。
ここでは、右辺値、左辺値、右辺値参照、左辺値参照、ムーブ、ユニバーサル参照、完全転送の意味を記載する。
右辺値
メソッドの戻り値、式の結果等は右辺値である。
class X;
int func();
int main()
{
X(); // クラスのコンストラクタは右辺値
func(); // func関数の戻り値は右辺値
int i = 1; // 変数iは左辺値, 1は右辺値
i + 1; // 式i + 1は右辺値
}
右辺値は左辺値に変換できないため、以下はコンパイルエラーとなる。
int main()
{
int x; // 変数xは左辺値
(x + 1) = 9; // コンパイルエラー
}
左辺値
左辺値の例
class X;
int main()
{
int i; // 変数iは左辺値
X x; // Xクラスのインスタンスは左辺値
X *ptr_X = &x; // Xクラスのポインタは左辺値
}
左辺値参照
左辺値参照は右辺値を参照することができないが、constが付加された左辺値参照は右辺値を参照することができる。
class X {};
void f(X &x) {}
void g(const X &x) {}
int main()
{
X x;
f(x); // コンパイル可能
// コピーコンストラクタの引数は左辺値参照
g(x); // コンパイルエラー
// Xクラスのインスタンスは左辺値のため、constが付加された引数には指定できない
g(X()); // コンパイル可能
// constが付加された引数は右辺値を参照できる
}
右辺値参照
右辺値参照とは、右辺値への参照である。 右辺値参照は、<データ型>&& <変数名>と記述する。
右辺値参照は型であるため、左辺値になることができる。 また、右辺値・左辺値に関わらず、参照を初期化することを束縛するという。
int func()
{
return 20;
}
int main()
{
int&& i = 10; // 右辺値10を右辺値参照変数iに束縛
int&& j = func(); // func関数の戻り値(右辺値)を右辺値参照変数jに束縛
cout << i << endl; // 10
cout << j << endl; // 20
i = j; // 右辺値参照は左辺値になることもできるため、コンパイル可能
cout << i << endl; // 20
cout << j << endl; // 20
}
右辺値参照は、ムーブで使用される.
ムーブとは、あるオブジェクト(左辺値や一時オブジェクト等)をコピーすることなく他の変数に割り当てる操作である。
例えば、aの値をbに代入する時、インスタンスのコピーが行われて、メモリ上に2つのインスタンスが存在することになる。
もし、aが指すインスタンスが以降で使用されない場合、aのポインタをbが持つだけでよいため、コピー操作は必要無い。
class Test;
Test a = Test();
Test b = a;
// aはこれ以降使用しない
ある変数が持つオブジェクトを別の変数に割り当てて、その変数からはオブジェクトを使わないようにする操作をムーブという。
ムーブは、ムーブコンストラクタやムーブ演算子を使用して表現する。
ムーブコンストラクタやムーブ演算子の引数に、右辺値参照が現れる。
ムーブに対して、コピー演算子やコピーコンストラクタの引数は、左辺値参照となる。
std::move
メソッドは、左辺値を右辺値にキャストするものである。
#include <iostream>
#include <utility>
class Counter
{
private:
int m_cnt;
public:
Counter() : m_cnt(0)
{
std::cout << "Default" << std::endl;
}
Counter(const Counter &c)
{
std::cout << "Copy" << std::endl;
}
Counter(Counter &&c)
{
m_cnt = c.getCnt();
std::cout << "Move" << std::endl;
}
~Counter() {}
int getCnt() {return m_cnt;}
};
int main()
{
Counter c1, // 引数無しのコンストラクタ
c2; // 引数無しのコンストラクタ
Counter c3(c1); // コピーコンストラクタ
Counter c4(std::move(c2)); // ムーブコンストラクタ
return 0;
}