1. 什么是右值? 有名称,可以取地址的值,是左值。 没有名称,不能取地址的值,就是右值,另外类似函数返回值这种临时变量,定义为将亡值,也是右值。 c++11中,所有的值,必属于左值,将亡值,和纯右值。
2. 左值引用,右值引用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int main() { int a = 0; //ok int& b = a; //ok,左值引用 int& c = 0; // not ok,左值引用无法引用右值 const int& d = 0; // ok,常左值引用,可以绑定右值 const int& e = a; // ok,常左值引用,可以绑定左值 int&& f = 0;//ok 右值引用绑定右值 const int && g = 0; // ok,常右值引用可以绑定右值 //但是实际上没有意义,因为绑定的右值无法修改,一般右值引用是为了实现移动语义,降低copy消耗 int&& h = a;//not ok,右值引用无法绑定左值 return 0; }
左值引用,只能绑定左值 常左值引用,可以绑定常量左值,右值,非常量左值和右值 右值引用,只能绑定非常量右值 常右值引用,可以绑定常量右值,非常量右值
3. 讨论右值引用,要注意排除返回值优化 如果关闭返回值优化,可以参考https://www.yhspy.com/2019/09/01/C-%E7%BC%96%E8%AF%91%E5%99%A8%E4%BC%98%E5%8C%96%E4%B9%8B-RVO-%E4%B8%8E-NRVO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Person GetPerson(){ return Person(); } int main() { Person person = GetPerson(); person.print(); /* 一共执行三次构造 1 Person()默认构造函数 2 GetPerson函数返回时,生成临时对象,调用移动构造函数 3 使用临时对象,构造person,调用移动构造函数 */ return 0; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Person&& GetPerson(){ return Person(); } int main() { Person person = GetPerson(); person.print(); /* 一共执行两次次构造,这种写法是错的,会有warnning Returning reference to local temporary object 1 Person()默认构造函数 2 右值引用,引用了已经析构的临时对象 3 使用临时对象,构造person,调用移动构造函数 */ return 0; }
4. 函数返回值,如果没有写左值引用,就是临时变量属于右值 1 2 3 4 5 6 7 8 9 10 11 12 Person GetPerson(){ return Person(); } int main() { Person person1 = GetPerson(); //调用一次构造,两次次移动构造 Person&& person2 = GetPerson(); //调用一次构造,一次移动构造 return 0; }
理解上面person1和person2的区别,person1是根据临时变量构造了一个新的对象 person2是直接对临时变量的右值引用
注意 1 2 3 4 5 6 7 const Person& GetPerson1(){ return Person(); } Person&& GetPerson2(){ return Person(); }
上面两种写法都是错误的,返回的是临时变量的引用,可以编译通过,但是有警告
Returning reference to local temporary object