c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)

Posted xutopia

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)相关的知识,希望对你有一定的参考价值。

c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)

c++的左值,右值 精辟总结

当一个对象被用作右值的时候,使用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)左值右值,完美转发参考文档

左值持久,右值短暂;move:显示地将一个左值转换为对应右值的引用类型,还可以获取绑定到左值上的右值引用,int&& rr3 = std::move(rrl); 使用move就意味着除了对rrl赋值或销毁它外,我们不再使用它。

std::forward()与std::move()相区别的是,move()会无条件的将一个参数转换成右值,而forward()则会保留参数的左右值类型,可以使用std::forward实现完美转发

移动语义解决了无用拷贝的问题:移动构造函数

右值引用:函数的返回值

int& 左值引用

int&& 右值引用

c++中无用拷贝的情况

/*类里面 没有移动构造函数
这样就会使用 copy construct function,会导致大量无用的 memory copy。
*/
class Test   
public:
    string desc; 
    int * arrnullptr;
    Test():arr(new int[5000]1,2,3,4)  
        cout << "default constructor" << endl;
    
    Test(const Test & t) 
        cout << "copy constructor" << endl;
        if (arr == nullptr) arr = new int[5000];
        copy(t.arr,t.arr+5000, arr);
    
    ~Test()
        cout << "destructor " << desc << endl;
        delete [] arr;
    
;

Test createTest() 
    return Test();


int main()

    Test reusable;
    reusable.desc = "reusable";
    Test duplicated(reusable);
    duplicated.desc = "duplicated";

    Test t(createTest());
    t.desc = "t";

    cout<<"end"<<endl;


运行结果

default constructor
copy constructor
default constructor
end
destructor t
destructor duplicated
destructor reusable

使用移动语义避免无用的拷贝

/*使用移动 construct function,避免无用的memory copy。
*/

class Test    
    public:
    string desc;
    int * arrnullptr;
    Test():arr(new int[5000]1,2,3,4)  
        cout << "__default constructor" << endl;
    
    Test(const Test & t) 
        cout << "__copy constructor" << endl;
        if (arr == nullptr) arr = new int[5000]; //在这里要将 t.arr 置为空,因为经过move之后,我们认为不在使用这个值了,避免在新的对象中把指针释放后,原来的对象中存在野指针的现象
        copy(t.arr,t.arr+5000, arr);
    
    Test(Test && t): arr(t.arr) 
        cout << "__move constructor" << endl;
        t.arr = nullptr;
    
    ~Test()
        cout << "..destructor " << desc << endl;
        delete [] arr;
    
;

Test createTest(string str) 
    Test rt;
    rt.desc = str;
    cout<<"createTest:"<<&rt<<endl;
    return rt;


void main()
    Test reusable;
    reusable.desc = "reusable";
    cout<<"reusable.arr "<<reusable.arr<<endl;
    
    Test duplicated(std::move(reusable));
    duplicated.desc = "duplicated";
    cout<<"reusable.arr "<<reusable.arr<<endl;
    cout<<"duplicated.arr "<<duplicated.arr<<endl;

    cout<<"rvalue--"<<endl;
    Test&& rt1 = createTest("rval");      //使用右值引用接收
    cout<<"rt1.arr "<<rt1.arr<<endl;
    
    cout<<"no rvalue--"<<endl;
    Test rt2 = createTest("normalVal");      //不使用右值引用接收,可以看到这里比使用右值引用接收 多了一次构造和析构(createTest中的临时对象)
    cout<<"createTest:"<<&rt2<<endl;        //尴尬,其实这里编译器已经做了优化了,可以看到第地址一样
    cout<<"rt2.arr "<<rt2.arr<<endl;

    cout<<"end"<<endl;

输出结果

__default constructor
reusable.arr 0x56521b946e70
__move constructor
reusable.arr 0
duplicated.arr 0x56521b946e70
rvalue--
__default constructor
createTest:0x7ffd092ea390
rt1.arr 0x56521b94c0b0
no rvalue--
__default constructor
createTest:0x7ffd092ea3c0
createTest:0x7ffd092ea3c0
rt2.arr 0x56521b950ee0
end
..destructor normalVal
..destructor rval
..destructor duplicated
..destructor reusable

左值引用右值引用

//左值引用和右值引用
void foo(const int & i)  cout << "const int & " << i << endl; 
void foo(int & i)   cout << "int & " << i << endl; 
void foo(int && i)  cout << "int && " << i << endl; 
void foo(const int && i)  cout << "const int && " << i << endl; 
void main()
    int i = 2;
    foo(i);
    foo(2);
    foo([]()->const int && return 2;());

完美转发

/*在main当中调用relay,Test的临时对象作为一个右值传入relay,在relay当中又被转发给了func,那这时候转发
给func的参数t也应当是一个右值。也就是说,我们希望:当relay的参数是右值的时候,func的参数也是右值;当
relay的参数是左值的时候,func的参数也是左值。
*/
class Test    
    public:
    int * arrnullptr;
    Test():arr(new int[5000]1,2,3,4)  
        cout << "default constructor" << endl;
    
    Test(const Test & t) 
        cout << "copy constructor" << endl;
        if (arr == nullptr) arr = new int[5000];
        copy(t.arr,t.arr+5000, arr);
    
    Test(Test && t): arr(t.arr) 
        cout << "move constructor" << endl;
        t.arr = nullptr;
    
    ~Test()
        cout << "destructor" << endl;
        delete [] arr;
    
;

template <typename T>
void func(T t) 
    cout << "in func" << endl;


template <typename T>
void relay(T&& t) 
    cout << "in relay" << endl;
    func(t);

//完美转发
template <typename T>
void relay1(T&& t) 
    cout << "in relay " << endl;
    func(std::forward<T>(t));


void main() 
    // relay(Test());
    // cout<<"end"<<endl;

    relay1(Test());
    cout<<"end"<<endl;


更多编程资料见公众号 xutopia77

以上是关于c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)的主要内容,如果未能解决你的问题,请参考以下文章

对左值(lvalue)和右值(rvalue)的两种理解方式

左值的详解

如何评价 C++11 的右值引用(Rvalue reference)特性?

C++左值右值和构造函数们

C++,在函数中采用 const lvalue 和 rvalue 引用

C++右值引用万能引用完美转发和引用折叠