C/C++教程

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

本文主要是介绍c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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

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

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

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

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

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

右值引用:函数的返回值

int& 左值引用

int&& 右值引用

c++中无用拷贝的情况

 /*类里面 没有移动构造函数
 这样就会使用 copy construct function,会导致大量无用的 memory copy。
 */
 class Test {  
 public:
     string desc; 
     int * arr{nullptr};
     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 * arr{nullptr};
     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 * arr{nullptr};
     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;
 ​
 }
这篇关于c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!