左值、右值与右值引用

在C语言中,我们常常会提起左值(lvalue)、右值(rvalue)这样的称呼。而在编译程序时,编译器有时也会在错误的信息中包含左值、右值的说法。不过左值、右值通常不是通过一个严谨的定义而为人所知的,大多数时候左右值的定义与判别方法是一体的。一个典型的判别方法就是,在赋值表达式中,出现在等号左边的就是“左值”,而在等号右边的,则成为“右值”。比如:

a = b + c

在这个赋值表达式中,a就是一个左值,而b+c则是一个右值。这种识别左值、右值的方法在C++中依然有效。不过C++中还有一个被广泛认同的说法,那就是可以取地址的、有名字的就是左值,反之不能取地址的、没有名字的就是右值。那么这个加法赋值表达式中,&a是允许的操作,但&(b+c)这样的操作则不会通过编译。因此a是一个左值,(b+c)是一个右值。

这些判别方法通常都非常有效。更为细致地,在C++11中,右值是由两个概念构成的,一个将亡值(xvalue,eXpiring Value),另一个则是纯右值(prvalue,Pure RValue)。

其实纯右值就是C++98标准中右值的概念,讲的是用于辨识临时变量和一些不跟对象关联的值,比如

  1. 非引用返回的函数返回的临时变量值
  2. 一些算数表达式,比如1 + 3产生的临时变量值
  3. 不与对象关联的字面量值,比如:2、‘c’、true
  4. 类型转换函数的返回值
  5. Lambda表达式

而将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移位他用),比如

  1. 返回右值引用T&&的函数返回值
  2. std::move的返回值
  3. 转换为T&&的类型转换函数的返回值

而剩余的,可以标识函数、对象的值都属于左值。在C++11的程序中,所有的值必属于左值、将亡值、纯右值三者之一。

通常情况下,右值引用是不能绑定到任何的左值的。而在C++98标准中就已经出现的左值引用是否可以绑定到右值(由右值进行初始化)?比如:

T &e = ReturnRvalue();
const T &F = ReturnRvalue();

这样的语句是否能够通过编译呢?这里的答案是:e的初始化会导致编译时错误(注:我在VS2017上是可以编译通过),而f则不会。

出现这样的状况的原因是,在常量左值引用在C++98标准中开始就是个“万能”的引用类型。他可以接受非常量左值常量左值、右值对其进行初始化。而且在使用右值对其初始化的时候,常量左值引用还可以像右值引用一样将右值的生命期延长。不过相比于右值引用所引用的右值,常量左值所引用的右值在它的“余生”中只能是只读的。相对地,非常量左值只能接受非常量左值对其进行初始化。

在C++98通过左值引用来绑定一个右值的情况并不少见,比如:

const bool & judgement = true;
const bool judgement = true;

可能很多程序员都没有注意到其中的差别(从语法上讲,前者直接使用了右值并为其“续命”,而后者的右值在表达式结束后就销毁了)。

事实上,即使在C++98中,我们也常可以使用常量引用来减少临时对象的开销,例子如下:

class Copyable
{
public:
	Copyable() {};
	Copyable(const Copyable&)
	{
		cout << "Copied" << endl;
	}
};

Copyable ReturnRvale()
{
	return Copyable();
}

void AcceptVal(Copyable )
{

}

void AcceptRef(const Copyable &)
{

}

int main()
{
	cout << "Pass by Value" << endl;
	AcceptVal(ReturnRvale());
	cout << "Pass by Ref" << endl;
	AcceptRef(ReturnRvale());
	return 0;
}

AcceptVal使用了值传递参数,而AcceptRef使用了引用传递。在以ReturnRvalue返回的右值为参数的时候,AcceptRef就可以直接使用产生的临时值(并延长其生命期),而AcceptVal则不能直接使用临时对象。

结果如下:

Pass by Value
Copied
copied
Pass by Ref
Copied

在C++11中,同样地如果以右值引用为参数声明如下函数:

void AcceptRvalueRef(Copyable &&){}

这样也可以减少临时变量拷贝的开销,还可以在AcceptRvalueRef中修改改临时值。

参考:《深入理解C++11》

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值