Master std::move and std::forward

Before explaining std::move and std::forward, we must first understand the concepts of lvalue and rvalue in C++.

1. Lvalue, rvalue

(1) Lvalue: generally refers to the value that has a corresponding storage unit in the memory, the most common one is the variable created in the program.
(2) Rvalue: generally refers to a value that does not have a corresponding storage unit (immediate value in a register, intermediate result, etc.), such as a constant, or a temporary variable for expression calculation.

To determine whether an expression is an lvalue or an rvalue:

(1) An expression that can be located on the left side of = is an lvalue; conversely, an expression that can only be located on the right side of = is an rvalue.

// 其中x是一个左值,字面值5是一个右值
int x = 5;

// 错误,5 不能为左值
5 = x;

Explanation: Lvalues ​​in C++ can also be used as rvalues.

// y 是一个左值
int y = 10;

// x、y 都是左值,但可以将 y 可以当做右值使用
x = y;

(2) An expression with a name that can obtain the storage address is an lvalue; otherwise, it is an rvalue.

In the above example, the variables x and y are variable names and their storage addresses can be obtained through &x and &y, so x and y are both lvalues; on the contrary, the literals 5 and 10 have neither names nor their storage addresses , so 5 and 10 are rvalues.

2. Lvalue reference, rvalue reference

(1) Lvalue reference: C++ uses & to refer to variables. The reference we are usually familiar with is lvalue reference.

    int num = 10;
    int& b = num; //正确
    int& c = 10; //错误

    const int& c = 10; //正确

(2) Rvalue references:

Because the rvalue itself has no corresponding storage unit, it cannot be referenced. Rvalue references are actually just a logical concept. The biggest function is to make an lvalue similar to an rvalue (the following program example), so that the transfer between variables is more in line with "semantic transfer" to reduce the transfer The overhead of multiple copies between. The rvalue reference symbol is &&.

Note:
(1) Rvalue references do not support lvalue references;
(2) Non-const rvalue references can only refer to non-const rvalues;
(3) Constant rvalues ​​can refer to constant rvalues ​​and non-const rvalues .

int num1 = 10;
const int num2 = 100;

int&& a = num1;	//编译失败,非常量右值引用不支持引用非常量左值
int&& b = num2;	//编译失败,非常量右值引用不支持引用常量左值
int&& c = 10;	//编译成功,非常量右值引用支持引用非常量右值

const int&& d = num1;	//编译失败,常量右值引用不支持引用非常量左值
const int&& e = num2;	//编译失败,常量右值引用不支持引用常量左值
const int&& f = 100;	//编译成功,常量右值引用支持引用右值

3. move() function

The move() function coerces an lvalue to an rvalue . But move() does not have the ability to move!

int num = 10;
int&& a = std::move(num);  //编译成功
std::cout << a << std::endl;   //输出结果为10;
std::cout << num << std::endl; //输出结果为10;move只是把左值强制转换成右值,并没有移动能力。

a = 20;
std::cout << num << std::endl; //输出20

In order to deepen the impression of move(), let's look at another example:

std::string str1 = "I love C++";
std::string str2 = std::move(str1);
std::cout << "str2:" << str2 << std::endl;
std::cout << "str1:" << str1 << std::endl;

Results of the:

We see that after the move() function is executed, the content in str1 is gone, is it removed? We said earlier that move() has no ability to move, and the reason why the content of str1 is empty is the "move constructor" of string. Let's look at another example:

std::string str3 = "I love C";
std::string&& def = std::move(str3);
std::cout << "str3:" << str3 << std::endl;
std::cout << "def:" << def << std::endl;

Results of the:

4. forward() function

The role of std::forward is perfect forwarding. If an lvalue is passed, it is an lvalue reference, and if an rvalue is passed, it is an rvalue reference.

std::move can reduce unnecessary copy overhead and improve program efficiency. But the role of std::forward is to forward, lvalue references are forwarded to lvalue references, rvalue references or rvalue references, what is its meaning?

It turns out that during the execution of the program, there will actually be additional implicit conversions for the passing of references. An rvalue reference parameter may be converted into an lvalue reference after being forwarded by a function call, but this is not what we want to see the result of.

Example:

#include <iostream>

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

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

template<typename T>
void testForward(T&& v) {
    print(v);
    print(std::forward<T>(v));
    print(std::move(v));
}

int main(int argc, char* argv[])
{
    testForward(1); // 传入右值

    std::cout << "======================" << std::endl;

    int x = 1;
    testForward(x); // 传入左值
}

Results of the:

 

reference:

(1) [C++ Features] Understanding of std::move and std::forward

(2) [C++] Lvalue and rvalue, lvalue reference (&) and rvalue reference (&&)

(3) Copying Mr. Li Chao's std::forward()

Guess you like

Origin blog.csdn.net/mars21/article/details/131678322