左と右の値値値の参照は、左と右の基準値ははstd ::移動のstd ::前方

1.左の値と右の値

値のアドレスを取ることができますことは、左、または右に値されます

int foo() {
  return 0;
}

int a = 1;       //a左值, &a有效
int &&b = 1;  //b左值, &b有效
a + 1;           //a + 1右值,&(a+1)无效
foo();           //foo()右值, &(foo())无效
1;                //右值, &1无效
int c = b + a;//a, b, c都是左值,不要以出现在等号左右来区分左值右值

2.左側の基準値、左に一定基準の値、名前の右辺値参照、名前右辺値参照

左基準値、右辺値参照は名前の左側に定数の参照、の値は、無名の右辺値参照:4つの参照があります。

  1. 左基準値T&:左辺値参照するメモリアドレスのニーズは、それゆえ、唯一のバインド値が左にすることができます。
  2. T&constの定数左辺値参照:一定の左辺値参照値は、左または右の値をバインドすることができます。
  3. 変数名の値を参照すると、右、できるだけバインド正しい値:右辺値参照T &&名前付き。
  4. 名前右辺値参照ははstd ::移動(T):変数名が値が左または右の値をバインドすることはできません、右基準値ではありませんが。
できるバインド 左辺値 右辺値
左基準値 はい ノー
左一定の基準値 はい はい
名前付き右辺値参照 ノー はい
名前右辺値参照 ノー ノー
int foo() {
  return 0;
}

int a = 0;
int &b = a;                 //可以, 左值引用绑定左值
int &c = 1;                  //不行,左值引用不能绑定右值
int &d = foo();            //不行,左值引用不能绑定右值
const int &e = a;        //可以,常量左值引用可以绑定左值
const int &f = foo();   //可以,常量左值引用可以绑定右值
int && g = b;             //不可以,具名右值引用不可以绑定左值
int && h = foo();        //可以,具名右值引用绑定右值
int && i = a + a;        //可以,具名右值引用绑定右值
std::move(a) = 1;        //不可以,无名右值引用不能绑定左值或者右值
int && j = std::move(a);//可以,具名右值引用绑定无名右值引用

左基準値、左に定数参照の値、および名前の右辺値参照の三つの変数の型であり、int型、char型、値が残っている限り、同じアドレスを持っています。無名の右辺値参照にはアドレスは、左の値に属していません。

実装では、と引用された左の値が同じではない、無名の右辺値参照は、そのメモリ参照の内容のためのスペースを開きます。このとき、基準値は、コンテンツアドレスのために必要と右側空間と命名される。例えば、以下の例のように(x64の8バイト)+コンテンツサイズは、スタック上の前、12バイト、4バイトの記憶領域(INT)1を開きます8バイトのアドレスに格納されたコンテンツ

int main() {
  int &&a = 1;
  return 0;
}

これは、コンパイルされています

        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $1, %eax
        movl    %eax, -12(%rbp)     ;  [rbp - 12 , rbp - 8) <-- (int)1
        leaq    -12(%rbp), %rax      ;  取地址,rax = rbp - 12
        movq    %rax, -8(%rbp)      ; 存地址 [rbp - 8, rbp) <--- rbp - 12
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc

したがって、我々は最初の変数、次の出力例2に適用しなければならない左と同じ値を参照せずに、名前右辺値参照の内容を変更することができ

#include <iostream>
int foo() {
  return 0;
}
int main() {
  int &&a = foo();
  a = 2;
  std::cout<<a<<std::endl;
}

だから、右辺値参照を名前の操作では、我々は右の値を変更することができ、参照自体の値は、直接右で命名変更することができ、あなたは他の変更がT&T = &&方法によって参照させることができます

3.のstd ::移動

左または右の値が値無名の右辺値参照となりますためのstd ::動きが担当しています。移動の実装を初めて目:

template <class typename>
struct remove_reference { typedef T type; };

template <class typename>
struct remove_reference<T&> { typedef T type; };

template <class typename>
struct remove_reference<T&&> { typedef T type; };

template<typename T>
typename std::remove_reference<T>::type&& move(T&& t) { 
  return static_cast<typename std::remove_reference<T>::type&&>(t); 
}

STD ::このようINT&int型への変更などの型への参照を削除する責任があるremove_reference、

私たちは、参照右の入力は名前の基準値で移動し、出力は名前の右辺値参照であることができます。Oの違いは何ですか?T自体は、非基準intとして、入力されてもよい、はstd ::文字列、また、スタンダード::文字列と、INTを&&引用し、出力のみ未知の右辺値参照であることができます。この変更を理解するために、最初に次のコードをエンドTに入力を検出することはどのようなタイプであります

#include <iostream>
#include <string>
template<typename T>
bool T_is_value(T && x) {
  using U = typename std::remove_reference<T>::type;
  return std::is_same<T, U>::value && (!std::is_const<U>::value);
}


template<typename T>
bool T_is_const_value(T && x) {
  using U = typename std::remove_reference<T>::type;
  return std::is_same<T, U>::value && std::is_const<U>::value;
}

template<typename T>
bool T_is_left_ref(T && x) {
  using U = typename std::remove_reference<T>::type&;
  using V = typename std::remove_reference<T>::type;
  return std::is_same<T, U>::value && (!std::is_const<V>::value);
}

template<typename T>
bool T_is_const_left_ref(T && x) {
  using U = typename std::remove_reference<T>::type&;
  using V = typename std::remove_reference<T>::type;
  return std::is_same<T, U>::value && std::is_const<V>::value;
}

template<typename T>
bool T_is_right_ref(T && x) {
  using U = typename std::remove_reference<T>::type&&;
  using V = typename std::remove_reference<T>::type;
  return std::is_same<T, U>::value && (!std::is_const<V>::value);
}
template<typename T>
bool T_is_const_right_ref(T && x) {
  using U = typename std::remove_reference<T>::type&&;
  using V = typename std::remove_reference<T>::type;
  return std::is_same<T, U>::value && std::is_const<V>::value;
}

class A {};

int main () {
  const A a;
  std::cout<<"T_is_value           "<<T_is_value(a)<<std::endl;
  std::cout<<"T_is_const_value     "<<T_is_const_value(a)<<std::endl;
  std::cout<<"T_is_left_ref        "<<T_is_left_ref(a)<<std::endl;
  std::cout<<"T_is_left_const_ref  "<<T_is_const_left_ref(a)<<std::endl;
  std::cout<<"T_is_right_ref       "<<T_is_right_ref(a)<<std::endl;
  std::cout<<"T_is_const_right_ref "<<T_is_const_right_ref(a)<<std::endl;
  return 0;
}

輸出

T_is_value           0
T_is_const_value     0
T_is_left_ref        0
T_is_left_const_ref  1
T_is_right_ref       0
T_is_const_right_ref 0

従ってTは、A&const参照型T左定数値は=です。エントリサブルーチンパラメータは、実際の入力引数がCONST A&&&であるので、コンパイラは、このタイプの取得方法であり、右辺値参照型T &&と命名されているので?まず、基準以下のC ++参照の崩壊:

T && & = T &  
T& && = T &
T && && = T &&

Tのconstが持ついずれかの揮発性であることができます

ここでは例を見てです:

  1. A const型、T =定数Aは、次いで、パラメータがとして渡された場合:CONST A && T =、許可されていない、左側の値は、正しい値が指定された参照に結合することができないからです。
  2. その後、A&&& T = A CONST引数として渡されたA&、もしT =定数、前の基準崩壊規則に従って、左側に結合され、左側の基準値のCONST A&T = A、一定の値を有します。
  3. もしT =定数A &&、崩壊ルールによって得られるのconst A && T = A、左命名基準結合値の正しい値、不可能です。

だから、コンパイラは2番目のオプションを使用しています

  1. 左に基準値の場合、上記メイン関数:B; A&CONST = B、次いでT =定数A&
  2. 変更STDの上記主な機能であれば::移動()、その後、T =定数A

注無名の右辺値参照に予約のconst /揮発この機能、機能は持っているn個のパラメータは、それぞれがオーバーライドのconst /非const参照にする必要がありますとき、私たちはこれらの2つのタイプを統一する&& Tを使用することができます

4のstd ::前方

上記の例から、我々は、関数のパラメータの値が常に残されているので、参照のことなので、関数呼び出しの後、無名の右辺値は姿を消しました。次のコードを考えてみましょう

#include <iostream>

template <typename T>
void print(T &t) {
  std::cout<<"T &t"<<std::endl;
}


template <typename T>
void print(const T &t) {
  std::cout<<"const T &t"<<std::endl;
}

template <typename T>
void print(T &&t) {
  std::cout<<"T &&t"<<std::endl;
}


template <typename T>
void forward_value(T&& val) {
  print(val);
}

class A {};
int main () {
  A a;
  forward_value(a);
  forward_value(std::move(a));
  return 0;
}

輸出

T &t
T &t

この機能を無効印刷(T && t)を呼び出すことができず、これを変更する方法?

std ::前方にこの問題を解決するために、コードは、以下の変更を行います。

template <typename T>
void forward_value(T&& val) {
  print(std::forward<T>(val));
}

出力

T &t
T &&t

STDは::前方にこれを行う方法ですか?実装を見てください

template<typename T>
T&& forward(typename std::remove_reference<T>::type& t) { 
  return static_cast<T&&>(t); 
}
 
template<typename T> 
T&& forward(typename std::remove_reference<T>::type&& t) {
  static_assert(!std::is_lvalue_reference<T>::value, "template argument"
            " substituting _Tp is an lvalue reference type");
  return static_cast<T&&>(t);

明らかに、つまり、Tは、再び電源を入れ&&。上記の例を考えてみましょう、

  1. forward_value()。=>のstd ::フォワード<A &>(ヴァル))。=>リターンはstatic_cast <T&&&>(T)。=>印刷(A&)
  2. forward_value(STD ::移動()); =>のstd ::フォワード(ヴァル))。=>リターンはstatic_cast <T &&>(T)。=>印刷(A &&)

おすすめ

転載: www.cnblogs.com/qxred/p/std_move_forward.html