C++中的值与引用

本文探讨了C++11引入的右值引用,包括左值、右值和它们的引用类型。介绍了左值和右值的不同解释,并讨论了引用的叠加性。移动语义通过std::move实现,而完美转发由std::forward完成,解决右值引用在特定情况下的转换问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

值与引用,准确的说是C++11后的值与引用。

因为这时候才明确的出现了右值引用,然后能讨论的东西就出来了,

那就是 左值、右值当然还有对应的左值引用和右值引用。

我第一次接触右值引用的时候是在看SFINAE相关方面的内容时,

当我第一次看到了 auto&& 这种写法,一脸蒙蔽,这是啥啊?

好在有标准文档的帮助,让我了解到了右值引用,但随之而来的,移动语义和完美转发等术语真的让我头大。

可不懂就得学啊,经过一段时间的摸索,终于对这些概念有了较清楚的认识了。

左值右值,通俗的讲就是左值是可以出现在等号的左边,能够被赋值,除了左值以外都是右值,也可以理解为出现在等号右边的值。

铺开来讲,你可能听说过:亡值,泛左值,纯右值 等术语。

有以下定义:

在c++11以后,表达式按值类别分,必然属于以下三者之一:左值(left value,lvalue),将亡值(expiring value,xvalue),纯右值(pure rvalue,pralue)。其中,左值和将亡值合称泛左值(generalized lvalue,glvalue),纯右值和亡值合称右值(right value,rvalue)。见下图,

左值右值有很多种方法解释:

除了上面说的通俗的说法,还有

左值:可以取地址并且有名字的东西就是左值。

右值:不能取地址的没有名字的东西就是右值。

还有

当一个对象被用作右值的时候,用的是对象的值(内容);

当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。

总之,单说一个变量是左值右值需要根据上下文分析,才能分析出来,真正有意义的是结合引用。

=========================================================================

引用:

T& 引用是在最原始的c++98就存在着,就是左值引用而T&& 是C++11新出现的概念,表示右值引用。

左值引用能绑定到左值上,右值引用能绑定到右值上,这是理所当然的事情,没什么可详细讨论的。

这里再介绍一些别的内容:

引用符合叠加性

std::add_lvalue_reference<T&>::type 是 T&

std::add_lvalue_reference<T&&>::type 是 T&

std::add_rvalue_reference<T&>::type 是 T&

std::add_rvalue_reference<T&&>::type 是 T&&

也就是说

T&& & => T&

T&& && => T&&

T& & => T&

T& && => T&

但是代码里不能这么写(vs 2017)

int b = 10;

int&& &a = b; //Error,但右值引用的左值引用是左值引用,理论上可以绑定到b上,

不过这么写就可以


typedef int&& intR;

typedef intR& intRR;

int main()

{

int &&_42 = 42;



int b = 10;

intRR a = b; //编译成功

}

移动语义和完美转发

这两个含义各有一个标准库的代表,std::move 代表了移动语义,std::forward 代表了完美转发。

std::move产生一个对象的将亡值,以供合理的调用对象的移动构造函数用。

std::forward 下面的列子里会提到为什么要有完美转发。

================================================================

那么一个终极问题:

右值引用是左值还是右值

答案是:看情况。

在一篇文章里说到

Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.

也是说右值引用是否有名字,来判定是左值还是右值。

测试代码如下:

class BASE

{

public:

BASE() {};

BASE(BASE const& rhs) {

cout << "aaaa\n";

};

BASE(BASE && rhs) {

cout << "bbbb\n";

};

};





class Derived :BASE

{

public:

Derived() {};

Derived(Derived const& rhs) :BASE(rhs) {};

Derived(Derived&& rhs):BASE(rhs) {

cout << "ddddd\n";

};

};



int main()

{

Derived temp(std::move(Derived()));

}

 

std::move(Derived()) 返回值是右值引用,但是Derived temp(std::move(Derived())); 并没有新“名字”接受std::move(Derived())产生的结果,所以,右值引用是右值,会调用Derived(Derived&& rhs)重载,但是在调用构造函数时,这个右值引用被赋予了名字“rhs”,BASE(rhs)此时变成了左值,BASE结果就调用了BASE(BASE const& rhs) 构造函数。

这个例子就阐述了为什么需要完美转发,可能本意就是想调用基类的移动构造函数,但是由于右值引用值的特性导致了一般想当然的写法无法实现。此时就需要用到了std::forward

Derived(Derived&& rhs):BASE(std::forward<Derived>(rhs)) {

cout << "ddddd\n";

};

这样就可以调用基类的移动构造函数了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值