左值和右值(L值和R值)
基础概念
int RGetValue()
{
return 10;
}
int& LGetValue()
{
static int value = 10;
return value;
}
int main()
{
//1.很多时候就像这个,左值就好像是在等号的左边
//右值在等号的右边,但这并不是一个完整的事实(实际上也不是这样)
//在这个例子中,i在内存中有地址,而10只是一个值,没有存储空间,直到它分配给L值
//所以,你不能给R值赋值。
int i = 10;
//2.这俩个a,和i都是左值。
int a = i;
//3.在这个例子中,RGetValue返回的也是一个R值,因为即使返回一个int,它在内存中也没有位置。
//在这里我们做的是,获得一个右值,将该临时值赋值并存储为L值。
int i = RGetValue();
//在这个例子中,我们在函数返回的就是一个左值了,(我们定义了一个静态的函数对象的引用并返回)
LGetValue() = 5;
}
关于引用
1、左右值都可以做参数
void SetValue(int value)
{
}
int main()
{
int i = 10;
//在这个带参数的例子中,我们可以用L值做参数,也可以用R值做参数
//但是在用R值做参数的情况下,当函数实际被调用时,这个R值将会被用来创建L值
SetValue(i);
SetValue(10);
}
2、左值参考
void SetValue(int& value)
{
}
int main()
{
int i = 10;
//一旦调用函数的参数是一个左值引用,这里就会立即出现一个错误
SetValue(10);
}
在这个错误提示中,有一个很特殊的规则,就是const。
当我们试图写int& a = 10;
,这是不被允许的,但是如果我们写const int&a = 10;
,这样就可行了。这个特殊的规则实际上编译器做的事情可能就像是
int temp = 10;
const int& a = temp;
实际上这只是避免去创建一个L值,而是仅仅能都支持L值和R值
所以如果我们想让开始的左值引用代码生效,我们可以这样改动它
//注意,我们在这里让参数等于const int&
void SetValue(const int& value)
{
}
int main()
{
int i = 10;
//这样就都支持了!
SetValue(i);
SetValue(10);
}
3、右值引用
如果我们想写一个仅接受临时对象的函数呢,我们就需要所谓右值引用&&
了!
void PrintName(std::string&& name)
{
std::cout << name << std::endl;
}
int main()
{
std::string firstName = "Yan";
std::string lastName = "Chernikov";
std::string fullName = firstName + lastName;
PrintName(firstName);
}
这是有意义的,因为我们可以编写此函数的重载
void PrintName(const std::string& name)
{
std::cout << "[lvalue]" << name << std::endl;
}
void PrintName(std::string&& name)
{
std::cout << "[rvalue]" << name << std::endl;
}
int main()
{
std::string firstName = "Yan";
std::string lastName = "Chernikov";
std::string fullName = firstName + lastName;
PrintName(fullName);
PrintName(firstName + lastName);
}
这非常有用,不过是在另一个主题“移动语义”上啦。