左値(Lvalue)#
左値は、メモリ内に明確な位置を持つオブジェクトを指し、アドレスを取得できます。言い換えれば、左値は代入演算子の左側に出現できる式です。例えば、変数、配列要素、ポインタの逆参照などが左値です。
特徴:
- アドレスを取得できる:左値式の結果はオブジェクトであり、アドレス演算子
&
を使用してそのオブジェクトのアドレスを取得できます。 - 持続性:左値が表すオブジェクトは、式の評価が終了した後も存在し続け、そのスコープを超えるか、明示的に破棄されるまで存在します。
例:
int x = 10;
int *p = &x;
*p = 20;
右値(Rvalue)#
右値は、式の評価時に作成される一時的なオブジェクトを指し、通常は明確なメモリ位置を持たず、アドレスを取得できません。右値は通常、定数、一時オブジェクト、または戻り値です。
特徴:
- アドレスを取得できない:右値は固定のメモリ位置を持たないため、右値のアドレスを取得することはできません。
- 短命性:右値が表すオブジェクトは、式の評価が終了した後には存在しなくなります。
例:
int y = 5 + 3;
int z = y * 2;
右値参照の導入(Rvalue Reference)#
C++11 では、右値をより効率的に操作するために右値参照が導入されました。右値参照は&&
を使用して宣言します。右値をより効率的にキャッチして操作することを可能にし、不必要なコピーや移動操作を減らします。
右値参照の応用:
- ムーブセマンティクス(Move Semantics):ムーブコンストラクタとムーブ代入演算子を使用することで、リソースの所有権を移転することができ、リソースをコピーするのではなく、移動できます。
- パーフェクトフォワーディング(Perfect Forwarding):テンプレート関数内で右値参照と
std::forward
を使用することで、引数の完璧な転送を実現できます。
例:
class A {
public:
A() { std::cout << "Constructor" << std::endl; }
A(const A&) { std::cout << "Copy Constructor" << std::endl; }
A(A&&) { std::cout << "Move Constructor" << std::endl; }
};
void foo(A&& a) {
A b = std::move(a); // ムーブコンストラクタが呼び出される
}
int main() {
A a;
foo(std::move(a)); // aを右値参照に変換
return 0;
}
std::move(a)
は左値a
を右値参照に変換し、コピーコンストラクタではなくA
のムーブコンストラクタを呼び出すことで、不必要なコピーを減らし、効率を向上させます。