【tips】いまさら左辺値参照と右辺値参照

# 備忘録です

`int&`が左辺値参照、`int&&`が右辺値参照。
```.cpp
int x = 1;
int& a = 1;		//エラー
int&& b = 1;	//OK
int& c = x;		//OK
int&& d = x;	//エラー
```
(どのサイトにも似たような説明が書かれているので、詳細は省きます)

もともとあったのが左辺値参照(int&)
右辺値参照も代入できるようにしたのがconst左辺値参照(const int&)
```.cpp
int x = 1;
const int& e = 1;	//OK
const int& f = x;	//OK
```
const左辺値参照(const int&)では、左辺値参照と右辺値参照の区別がつかない。
そこでバージョンC++11から左辺値(x変数)を代入できないようにした右辺値参照(int &&)が登場。

# なぜ右辺値参照が必要なの？

関数を呼び分けるためです。
```.cpp
void f(int& a) { }
void f(int&& a) { }

int main() {
  int x = 1;
  f(1);		//int&&が呼ばれる
  f(x);		//int&が呼ばれる
}
```

# 関数を呼び分けて何が嬉しいの？

ムーブとコピーが実現できる。

```a.cpp
#include <utility>
#include <cstring>
class A {
public:
    char* addr;
    std::size_t size;
    A(A&& x) {	//右辺値参照(ムーブ)
        size = x.size;
        addr = x.addr;		//アドレスをそのまま代入
        x.addr = nullptr;	//x.addrをnullptrにしたので、xをデストラクトしてもdelete[]されない
    }
    A(A& x) {	//左辺値参照(コピー)
        size = x.size;
        addr = new char[size];		//新しくヒープ領域を確保
        std::memcpy(addr, x.addr, size);
    }
    A(int x) {
        size = x;
        addr = new char[size];
        std::memset(addr, 0, size);
    }
    ~A() {  //デストラクター
        if(addr != nullptr) {
            delete[](addr);
            addr = nullptr;
        }
    }
};

int main() {
    A x(1);			//A(int x)が呼ばれる
    A a = 1;		//A(int x)が呼ばれる
    A b = std::move(x);	//A(int&& x)が呼ばれる
    A c = a;		//A(int& x)が呼ばれる
    //x変数はb変数にムーブしたので、x変数のデストラクターではメモリ開放(delete[])されない
}
```

# std::move()とstd::forward()
std::move()は何でもかんでも右辺値参照にしてしまう。それだと困る場合はstd::forward()を使う。

```b.cpp
#include <iostream>
#include <utility>

void f(int& x)       { std::cout << "mached: &" << std::endl;}
void f(const int& x) { std::cout << "mached: const &" << std::endl;}
void f(int&& x)      { std::cout << "mached: &&" << std::endl; }
void f(const int&& x) { std::cout << "mached: const &&" << std::endl;}

template<typename T> void t1(T&& obj) { f(std::move(obj)); }
template<typename T> void t2(T&& obj) { f(std::forward<T>(obj)); }

int main()
{
    int a = 1;
    const int b = 2;
    int& c(a);
    const int& d(a);
    int&& e = std::move(a);
    const int&& f = std::move(a);

    std::cout << "std::move()" << std::endl;
    t1(3);      // mached: &&
    t1(a);      // mached: &&
    t1(b);      // mached: const &&
    t1(c);      // mached: &&
    t1(d);      // mached: const &&
    t1(e);      // mached: &&
    t1(f);      // mached: const &&

    std::cout << "std::forward()" << std::endl;
    t2(4);              // mached: &&
    t2(a);              // mached: &
    t2(b);              // mached: const &
    t2(c);              // mached: &
    t2(d);              // mached: const &
    t2(e);              // mached: &
    t2(f);              // mached: const &
    t2(std::move(e));   // mached: &&
    t2(std::move(f));   // mached: const &&
}
```

# std::move()とstd::forward()の実体

実体はstatic_castです。


```b.cpp
(差分のみ)

//std::move()の真似
template<typename T> typename std::remove_reference<T>::type&& u_move(T&& obj) { return static_cast<typename std::remove_reference<T>::type&&>(obj); }
//std::forward()の真似
template<typename T> T&& u_forward(typename std::remove_reference<T>::type& obj) { return static_cast<T&&>(obj); }

template<typename T> void t1(T&& obj) { f(u_move(obj)); }		//std::moveをu_moveに変更
template<typename T> void t2(T&& obj) { f(u_forward<T>(obj)); }	//std::forwardをu_forwardに変更
```

# ユニバーサル参照(Universal Reference)
```.cpp
template<typename T> void f(T&& x) {} //関数テンプレートのT&&型
template<typename T> class A { A(T&& x) { } }; //クラステンプレートのT&&型
template<typename T> T&& x1; //変数テンプレートのT&&型
template<typename T> using B = A<T&&>;	//エイリアステンプレート(型の別名)のT&&型
auto&& x2 = 1; //auto&&型
```
この場合の&&は通常の右辺値参照とは異なる動作をする。
ユニバーサル参照は俗称、C++17仕様において「転送参照(Forwarding Reference)」という正式名称が与えられた。