3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-04-10

備忘録です

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)」という正式名称が与えられた。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?