左值和右值
1、什么是左值和右值?
- 左值
左值是可以标识并且在内存中有位置的。通俗表达就是,能取到地址的就是左值。 - 右值
右值是临时的、无法取地址的。
也就是说,能取到地址的就是左值,反之就是右值。
下面这个例子,test就是左值,因为我们可以使用&test取到test的内存地址,而9就是一个右值,因为我们取不到地址。
那么,test2以及GetValue2(test+test2)中的“test+test2”是左值还是右值呢?
int GetValue()
{
return 10;//右值
}
void GetValue2(int value)
{
std::cout << "the value is : " << value << std::endl;
}
int main(int argc, char* argv[])
{
int test = 9;//test是左值,9是右值
int test2=10;
GetValue2(test+test2);
}
上面的例子中test2显然是左值,因为可以取到地址。而GetValue2(test+test2)中的“test+test2”则是右值,因为它是临时变量,我们取不到地址。
2、左值引用和右值引用
上面例子的 GetValue2(int value)既接受左值输入,又接受右值输入。C++既然对左值和右值做了区分,那么肯定二者有各自的应用场景。那么,有没有只能接受左值或者只能接受右值的写法呢?这么做的意义又是什么?二者各自的应用场景又是什么呢?别急,我们一一道来。
GetValue2(int value)既接受左值输入又接受右值输入,本质上是值传递,也就是无论是左值还是右值,都会生成一个副本传递给函数。如果不是副本传递,而是直接操作实参,那就只能使用指针或者引用了。由此,C++就引入了左值引用和右值引用。
2.1 写法
左值引用使用“&”标记,右值引用使用“&&”标记。见下面的示例。
//右值引用,只能接受右值输入
void SetValue1(int&& value)
{
std::cout << "the R_value is : " << value << std::endl;
}
//左值引用,只能接受左值输入
void SetValue1(int& value)
{
std::cout << "the L_value is : " << value << std::endl;
}
如果我们强行将一个左值赋值给右值引用,就会出现下图的错误。
而如果强行将一个右值赋值给左值引用,就会出现下图的错误。
那么如果我们既想使用引用传参的形式,又想既接受左值又接受右值呢?
——用const,写法如下例所示,
(很多程序使用const引用,一是不能改变引用的内容,二是为了兼容左值和右值)
//即接受右值又接受左值
void SetValue3(const int& value)
{
std::cout << "the value is : " << value << std::endl;
}
2.2 意义与使用场景
C++11之前的引用都是左值引用的形式。为了提高内存管理效率,在C++11中引入了右值引用,主要用于实现移动语义和完美转发。
- 移动语义
允许资源的所有权从一个对象转移到另一个对象,而无需进行深拷贝。(详细说明可看我另一篇博客:https://blog.csdn.net/Tulip_Alice/article/details/135606425) - 完美转发
先说说什么是转发,转发指的就是函数之间的参数传递(例如,函数f1接收了一个参数a,然后又把这个参数a传递给了其函数体内调用的另一个函数f2)。
完美转发就是在函数之间传递参数的过程中,参数在传递后的属性保持不变(左值仍是左值,右值仍是右值)