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

右辺値参照

C++

C++11では、右辺に置かれる定数リテラルや文字列リテラル、オブジェクトのコンストラクタや関数などは、右辺値と呼ぶ・・・。
一方、左辺に置かれる変数やオブジェクト、ポインタなどは左辺値と呼ぶ・・・。

そして、C++03以前では単に「参照」と呼ばれていたものは「左辺値参照」というふうに呼ばれるようになった・・・。
左辺値参照が、変数などの恒久的なオブジェクトの参照を指すのに対し、右辺値参照は定数などの一時的なオブジェクトの参照を指す・・・。

といってもよくわからないとおもうので、今回は右辺値参照宣言子で右辺値を参照する変数を宣言してみる・・・。

test.hで、

クラスclass_testのコンストラクタ、メンバ関数member_function、そしてクラスとは別にグローバル関数のglobal_function・・・。

test.cppで、

class_testのコンストラクタは"constructor"を出力するだけ・・・。
member_functionは10を返し、global_functionは100を返すだけ・・・。
特に意味はない・・・。

左辺値参照(これまでの参照)は、

<型> &<参照名> = <参照するオブジェクト>

なのに対し、右辺値参照は、

<型> &&<参照名> = <参照する左辺値>

と書く・・・。

で、main.cppは、

// ヘッダのインクルード
// 既定のヘッダ
#include <iostream> // C++標準入出力
// 独自のヘッダ
#include "test.h" // クラスclass_test

// main関数の定義
int main(){

  // 変数・オブジェクトの宣言・初期化
  int num = 1; // int型変数numを1に初期化.
  class_test test; // class_test型オブジェクトtest.

  // 左辺値参照.
  int& l_num = num; // 変数numは左辺値.
  int&& r_num = num; // 右辺値ではない.
  class_test& l_test = test; // オブジェクトtestは左辺値.
  class_test&& r_test = test; // 右辺値ではない.

  // 右辺値参照.
  int&& r_1 = 1; // 定数リテラルは右辺値.
  int& l_1 = 1; // 左辺値ではない.
  class_test&& r_test = class_test(); // コンストラクタclass_test()は右辺値.
  class_test& l_test2 = class_test(); // 左辺値ではない.
  int&& r_global = global_function(); // 関数global_function()は右辺値.
  int& l_global = global_function(); // 左辺値ではない.

  // 値の出力.
  std::cout << "l_num = " << l_num << std::endl; // l_numを出力.
  std::cout << "r_1 = " << r_1 << std::endl; // r_1を出力.
  std::cout << "l_test.member_function = " << l_test.member_function() << std::endl; // l_test.member_function()を出力.
  std::cout << "r_test.member_function = " << r_test.member_function() << std::endl; // r_test.member_function()を出力.
  std::cout << "r_global = " << r_global << std::endl; // r_globalを出力.

  // プログラムの終了
  return 0;

}

まずは、こんな感じで・・・。
変数、オブジェクト、定数、コンストラクタ、関数、のそれぞれの右辺値参照と左辺値参照を取れるか試してみる・・・。
でこれでコンパイルすると、

$ g++ -o main main.cpp test.cpp -std=c++11
main.cpp: 関数 ‘int main()’ 内:
main.cpp:16:17: エラー: cannot bind ‘int’ lvalue to ‘int&&’
   int&& r_num = num; // 右辺値ではない.
                 ^
main.cpp:18:25: エラー: cannot bind ‘class_test’ lvalue to ‘class_test&&’
   class_test&& r_test = test; // 右辺値ではない.
                         ^
main.cpp:22:14: エラー: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’
   int& l_1 = 1; // 左辺値ではない.
              ^
main.cpp:23:16: エラー: redeclaration of ‘class_test&& r_test’
   class_test&& r_test = class_test(); // コンストラクタclass_test()は右辺値.
                ^
main.cpp:18:16: エラー: ‘class_test&& r_test’ previously declared here
   class_test&& r_test = test; // 右辺値ではない.
                ^
main.cpp:24:36: エラー: invalid initialization of non-const reference of type ‘class_test&’ from an rvalue of type ‘class_test’
   class_test& l_test2 = class_test(); // 左辺値ではない.
                                    ^
main.cpp:26:35: エラー: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’
   int& l_global = global_function(); // 左辺値ではない.
                                   ^
$

こうなる・・・。
まずnumは変数なので右辺値参照は取れない・・・。
testもオブジェクトなので右辺値参照はエラー・・・。
1は定数なので左辺値参照がそもそもエラー・・・。
class_test()はコンストラクタで右辺値なのでr_test自体は通るが、ここでは再定義エラーになっている・・・。
testはオブジェクトなので左辺値・・・。ここも再定義エラー・・・。
l_test2は左辺値参照で取ろうとしてるが、class_test()はコンストラクタで右辺値なのでエラー・・・。
l_globalも左辺値参照で取ろうとしてるが、global_function()は関数で右辺値なのでエラー・・・。

なので、

とコメントすると、

$ g++ -o main main.cpp test.cpp -std=c++11
$ ./main
constructor
constructor
l_num = 1
r_1 = 1
l_test.member_function = 10
r_test.member_function = 10
r_global = 100
$

コンパイルが通って、実行できる・・・。
結果は値を参照できたってだけ・・・。

こんな感じで、左辺値は左辺値参照しか取れないし、右辺値は右辺値参照しか取れない・・・。
ちなみに右辺値を参照している参照変数そのものは左辺値なので注意・・・。(変数だからね・・・。)

Sample/cpp/rvalue_reference/rvalue_reference/src/rvalue_reference at master · bg1bgst333/Sample · GitHub