M.2 R-value references


https://www.learncpp.com/cpp-tutorial/rvalue-references/
第1章では,r−valueとr−valueについて述べた.
それに何も心配することはありません.
c++11の前に、これは有効な提案ですが、c++11のmoveの意味を理解するために、
このテーマは見直す必要がある

L-values and r-values


「value」の名前があるにもかかわらず、彼らはvalueの式の特性について話しています.特性ではありません.
c++のすべての式には2つの特性があります
1つはタイプ(タイプチェック用)
もう1つはvaluecategoryです(式の結果が次の式に割り当てられるかどうかなど、特定のタイプの構文をチェックするために使用されます).
c++03まではl-valueとr-valueの2つの値categyrしか使用できません
正確には、l-valueとr-valueとはかなり複雑です.
そこで、私たちの目的を満たす範囲で簡単に観察してみましょう.
l-valueは関数やobjectが最も簡単だと考えています.すべてのl-valueにaddressとして割り当てられたメモリがあります
元はl-valueと定義されていたが、付与式の左手に適切なvalueであったが、constキーワードが追加された後、l-valueは2つのサブカテゴリに分類される
修正可能l-valueと非修正l-value(const)
r-valueはl-valueのすべてのものではないと簡単に考えることができる.
たとえば、literal(e.g.5)、一時値(e.g.x+1)、anonymousobject(e.g.Class(5,2).r−valueは、通常、それらの値を評価し、式の役割ドメイン(役割ドメインを超えた場合die)を有し、値を割り当てることができない.
Moveの意味をサポートするために,c++11は3つの新しいカテゴリを紹介した.
pr-values, x-values, and gl-values. まず省略しましょう

L-value reference


c++11までは「reference」タイプの参照のみが存在します
ただしc++11ではl-valuereferenceと呼ぶ
L-valuereferenceは修正可能なl-valueにのみ初期化できます

constオブジェクトのL-value referenceはl-valueとr-valueに初期化できます
しかし、この値は変更できません.

constオブジェクトのL-vlaue参照は、パラメータのコピーを作成することなく、任意のタイプのパラメータ(l-valueまたはr-value)を関数に渡すことができるため、特に便利です.

R-value references


c++11に新しいタイプの参照が追加されました.これをr-valuereferenceと呼ぶ
r-valuereferenceはr-valuerollのみを初期化するように設計されている
l-value referenceはシングルアンペアを使用し、
r-valuereferenceはデュアルアンペアによって生成される
int x{ 5 };
int &lref{ x }; // l-value reference initialized with l-value x
int &&rref{ 5 }; // r-value reference initialized with r-value 5
r-valuereferenceはl-valueに初期化できません

r-valuereferenceには2つの有用な特性がある
第1に、rvrefは、初期化に用いるオブジェクトの寿命をr-valuerefの寿命まで延長する
第二に、non-const r-valuereferenceはr-valueの変更を許可する
例を見てみましょう
#include <iostream>

class Fraction
{
private:
	int m_numerator;
	int m_denominator;

public:
	Fraction(int numerator = 0, int denominator = 1) :
		m_numerator{ numerator }, m_denominator{ denominator }
	{
	}

	friend std::ostream& operator<<(std::ostream& out, const Fraction &f1)
	{
		out << f1.m_numerator << '/' << f1.m_denominator;
		return out;
	}
};

int main()
{
	auto &&rref{ Fraction{ 3, 5 } }; // r-value reference to temporary Fraction

    // f1 of operator<< binds to the temporary, no copies are created.
    std::cout << rref << '\n';

	return 0;
} // rref (and the temporary Fraction) goes out of scope here
以上のプログラムの出力は以下の通りです.
3/5
anonymous objectなので、通常は式の終了時に範囲外になります
しかしr-valuerefで初期化したのでblockが終わる前に寿命が延びた.
印刷に使用できます
次に例を示します
#include <iostream>

int main()
{
    int &&rref{ 5 }; // because we're initializing an r-value reference with a literal, a temporary with value 5 is created here
    rref = 10;
    std::cout << rref << '\n';

    return 0;
}
以上のプログラムの出力は以下の通りです.
10
literal vlaueを変更するのは変に見えるかもしれません
前に示したように、r-valuerefはliteralで初期化されているにもかかわらず修正することができる.
R-valuerefは前例のようによく使われていません

R-value references as function parameters


r-valuerefは関数パラメータとしてよく用いられる
これは、l-value、r-valueで異なる機能を過負荷する場合に便利です.
void fun(const int &lref) // l-value arguments will select this function
{
	std::cout << "l-value reference to const\n";
}

void fun(int &&rref) // r-value arguments will select this function
{
	std::cout << "r-value reference\n";
}

int main()
{
	int x{ 5 };
	fun(x); // l-value argument calls l-value version of function
	fun(5); // r-value argument calls r-value version of function

	return 0;
}
これは以下の出力を与える
l-value reference to const
r-value reference
ご覧のように、リロード関数はl-valueでl-valuerefに一致します.
r-valueの場合、これをr-valuerefと呼ぶ
いつ私たちはこのようになりたいですか?詳細については、次のレッスンを参照してください.
言うまでもなく、これはmoveの意味の重要な部分です.
興味深い例があります
int &&ref{ 5 };
fun(ref);
これでrefはr-valuerefを呼び出すが、l-valueバージョンのfun関数を呼び出す
r-valuerefですが、ref自体はl-value
これでは混乱しているように見えますが、私たちは以下のように理解しています.
Name-objectはl-value、Anonymous objectはr-value
type objectがr-valueかl-valueかは決定しません

Returning an r-value reference


l-valuerefでreturn値を設定していないように.
r-valuerefでもreturn値は設定しません
r-valuerefも同じで、範囲を離れた瞬間に寿命が終わるので