「C++の基礎 - 右辺値と左辺値」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
(ページの作成:「== 概要 == 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;
 }