76 C++ 右值引用

一、左值、右值

在C++中,所有的值不是左值,就是右值。左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束后就不再存在的临时对象。有名字的对象都是左值,右值没有名字。

还有一个可以区分左值和右值的便捷方法:看能不能对表达式取地址,如果能,则为左值,否则为右值。

左值没有再细分的了,但C++11扩展了右值的概念,将右值分为纯右值和将亡值

纯右值:

    • 非引用返回的临时变量,普通函数、lambda函数、类的成员函数以值的方式返回,都是纯右值;
    • 运算表达式产生的结果;
    • 字面常量(C风格字符串除外,它是地址)。

将亡值:与右值引用相关的表达式,例如:将要被移动的对象、T&&函数返回的值、std::move()的返回值、转换成T&&的类型的转换函数的返回值。

不懂纯右值和将亡值的区别其实也没关系,统一看作右值即可,不影响使用。

#include <iostream>
using namespace std;
//左值 右值
class AA
{
};
AA getTemp()
{
    return AA();
}

AA &getTemp2(AA &a)
{
    return a;
}
void test()
{
    int i = 3;          // i是左值,3是右值
    int j = i + 3;      // j是左值,i+8是右值
    AA a = getTemp();   // a是左值,getTemp()的返回值是右值(临时变量)
    AA b = getTemp2(a); // b是左值,getTemp2(a)的返回值是引用,是左值.
    AA c;
    getTemp2(a) = c;
}
int main()
{
    test();
    return 0;
}

二、左值引用、右值引用

C++98中的引用很常见,就是给变量取个别名,在C++11中,因为增加了右值引用(rvalue reference)的概念,所以C++98中的引用都称为了左值引用(lvalue reference)。

右值引用就是给右值取个名字。

语法:数据类型&& 变量名=右值;

#include <iostream>
using namespace std;
class AA
{
public:
    int m_a = 10;
};
AA getTemp()
{
    return AA();
}
void test()
{
    int &&a = 3;     // 3是右值
    int b = 1;       // b是左值
    int &&c = b + 1; // b+1是右值

    AA &&aa = getTemp(); // getTemp()的返回值是右值(临时变量)

    cout << "a=" << a << endl;
    cout << "c=" << c << endl;
    cout << "aa.m_a=" << aa.m_a << endl;
}
int main()
{
    test();
    return 0;
}

右值有了名字之后,就是一个普通变量,普通变量有名字,可以取地址。

在上面代码中,getTemp()函数的返回值,本来在表达式语句结束后,其生命周期就该结束了,但是通过右值引用获得了新生,它的生命周期将与右值引用类型变量aa的生命周期一样。

为什么要给临时变量续命呢?

因为临时变量在某些场景下还有继续利用的价值。

引入右值引用的主要目的是实现移动语义。

左值引用只能绑定(关联、指向)左值,右值引用只能绑定右值,如果绑定的不对,编译就会失败。

但是,常量左值引用却是个奇葩,它可以算是一个万能的引用类型,它可以绑定非常量左值、常量左值、右值,而且在绑定右值的时候,常量左值引用还可以像右值引用一样将右值的生命期延长,缺点是,只能读不能改。

int a = 1;

const int& ra = a; // a是非常量左值。

const int b = 1;

const int& rb = b; // b是常量左值。

const int& rc = 1; // 1是右值。

总结一下,其中T是一个具体类型:

1)左值引用, 使用 T&, 只能绑定左值。

2)右值引用, 使用 T&&, 只能绑定右值。

3)已命名的右值引用是左值。

4)常量左值,使用 const T&, 既可以绑定左值又可以绑定右值。

#include <iostream>
using namespace std;
//左值 右值
class AA
{
public:
    string m_a;
};
AA getTemp()
{
    return AA();
}

void test()
{
    int &&a = 3;
    int b = 78;
    int &&c = b + 1;
    // int &&d = c; //错误,因为右值有了名字之后就成了左值,所以再对左值进行右值引用,报错
    int &d = c; //正确

    AA &&aa = getTemp();
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
    cout << aa.m_a << endl;

    //常量左值引用
    int aaa = 1;
    int &bbb1 = aaa;
    const int &bbb2 = aaa;

    const int &bbb3 = 1;
    int &&ccc = 1;
}
int main()
{
    test();
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值