Left and right value value value references left and right reference value std :: move std :: forward

1. The left value and right value

Can take the address of the value is left, or the right value

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. Left reference value, the value of the constant references to the left, named rvalue references, unnamed rvalue references

There are four references: Left reference value, the value of the constant references to the left, named rvalue references, unnamed rvalue references

  1. Left reference value T &: lvalue reference memory address needs, therefore, can only bind value left.
  2. Constant lvalue reference const T &: constant lvalue reference value can bind the left or right value.
  3. Named rvalue references T &&: the right with reference to the value of the variable name, can only bind the right value.
  4. Unnamed rvalue references std :: move (T): variable name is not the right reference value, the value can not bind the left or right value.
Can Bind Lvalue Rvalue
Left reference value Yes No
Constant reference value left Yes Yes
Named rvalue references No Yes
Unnamed rvalue references No No
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);//可以,具名右值引用绑定无名右值引用

Left reference value, the value of the constant references to the left, and named rvalue references are the three variables types, and int, char, long have the same address, so the value is left. The unnamed rvalue reference no address does not belong to the left value.

In implementation, and left values ​​quoted are not the same, unnamed rvalue references will open up a space for the contents of their memory references. At this time, a reference value is named a right space required for the content address (x64: 8 bytes) + content size, such as the example below, on the stack opens up 12 bytes, 4 bytes of storage (int) 1, before 8 byte address stored content

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

It is compiled

        .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

Therefore, we can modify the contents of named rvalue reference, without reference to the same value as the left must first apply for a variable, the following example output 2

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

So in the operation named rvalue references, we can modify the right value, the value of the reference itself can be modified directly named by the right, you can let other modification referenced by T & = T && way

3. std::move

std :: move is responsible for the left or right value becomes a value unnamed rvalue references. First look at the implementation of the move:

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 :: remove_reference which is responsible for removing the references to types, such as changes to the int & int

We can see, move the right input is a named reference value, and the output is a named rvalue references. O What is the difference? T itself may be input, such as a non-reference int, std :: string, may also be cited such std :: string &, int &&, and the output is the only unknown rvalue references. To understand this change, first detecting the inputs in the end T with the following code is what type

#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;
}

Export

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

Thus T is a constant value left reference type T = const A &. Since the entry subroutine parameters are named rvalue reference type T &&, so the actual input argument is const A & &&, the compiler is how to obtain this type? First of all, C ++ reference collapse following criteria:

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

T const can be either volatile with

Here's look at an example:

  1. a type const A, if T = const A, then the parameters passed as: const A && t = a, which is not allowed, because the value of a left, a right value can not bind to the named references.
  2. If T = const A &, then passed as argument const A & && t = a, according to the previous reference collapse rule, have const A & t = a, the constant value of the left reference value which is bound to the left.
  3. If T = const A &&, obtained by the collapse rule const A && t = a, named left and right values ​​of reference binding value, is not possible.

So the compiler uses the second option

  1. If the above-mentioned main function of a reference value into the left: A b; const A & a = b, then T = const A &
  2. If the above-mentioned main function of a change std :: move (a), then T = const A

Note unnamed rvalue references reserved const / volatile this feature, when a function has n parameters, each will need to override const / non-const reference, we can use T && to unify these two types

4 std::forward

From the above example we see that, because the function parameter values ​​are always left, so after a function call, unnamed rvalue disappeared. Consider the following code

#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;
}

Export

T &t
T &t

This function void print (T && t) can not be invoked, and how to change this?

std :: forward to solve this problem, the code will make the following changes:

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

Output

T &t
T &&t

Std :: forward is how to do this? Look at the implementation

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);

Obviously, that is, T && turn again. Consider the example above,

  1. forward_value(a); => std::forward<A &>(val)); => return static_cast<T& &&>(t); => print(A &)
  2. forward_value(std::move(a)); => std::forward(val)); => return static_cast<T&&>(t); => print(A &&)

Guess you like

Origin www.cnblogs.com/qxred/p/std_move_forward.html
Recommended