c++ 左右值与引用折叠

C++ 增加了一个新的类型,右值引用,记作“&&”

 左值:是指在内存中有明确的地址,我们可以找到这块地址的数据(可取地址)

右值:只提供数据,无法找到地址(不可取地址)

所有有名字的都是左值,而右值是匿名的

C++11 中右值分为两种情况:一个是将亡值,另一个是纯右值:

将亡值:非引用返回的临时变量,运算表达式产生的临时变量,原始字面量,lambda 表达式等。

纯右值:与右值引用相关的表达式,比如:T&& 类型函数的返回值,std::move() 的返回值等。

右值引用就是对右值进行引用的类型。因为右值是匿名的,所以我们只能通过引用的方式找到它。无论是左值引用还是右值引用,都必须初始化,因为引用类型本身并不拥有所绑定对象的内存,只是该对象的一个别名。通过右值引用,该右值所占的内存又可以被使用。

int a = 3; 
int &b = a; //左值引用只能引用左值,右值引用只能引用右值 (除非const,使得右值生命周期延长) 
int &&c = 4; 
const int &d = 3;

右值引用的好处

右值引用只能绑定到一个将要销毁的对象。因此我们可以自由的将一个右值引用的资源移动到另一个对象中。

引入右值引用的主要目的是提高程序运行的效率。有些对象在复制时需要进行深复制,深复制往往非常耗时。合理使用右值引用可以避免没有必要的深复制操作。

通过move可以将一个对象转成右值,在调用的函数中,一个右值引用参数 接管这个对象。

class A
{
    public:
        int *a;
        A()
        {
            a = new int[10]{1,2,3};        
        }
        A (const A&) { a = new int[10]{1,2,3};}
};

void test(A&& w )   //直接使用接收的右值,没有再拷贝
{
    
}
A retA()
{
    A a;   //构造A对象
    return a;  //拷贝构造一份返回
}

int main()
{
    test(retA());
}

使用右值引用参数接收一个右值后,在函数中作为左值使用,因为被具名化了,如果需要继续作为右值使用,需要使用

std::forward函数,此函数实现保留参数原来的左右值属性

std::forward(t); t是参数,它是T类型的左或右值引用 func( std::forward(t) ); func的传入参数是保留原来属性的t ,有时也称为完美转发

class A
{
    public:
        int *a = new int[10]{1,2,3};
};

void test1(A&& w)
{
    
}

void test(A&& w )   //参数接收一个A的右值,具名化为w,作为左值使用
{
    w.a[1] = 10;
    test1(w);    //错误,直接使用w被视为左值了,即使它是一个右值引用参数
    test1( std::forward<A>(w) );  //forward保留原本的左右值属性,原本是右值
    test1(std::move(w));    //直接强制转换为右值
}

int main()
{
    A a;
    test(move(a));
}

C++中普通函数在不带const时,左值引用只能接收左值,右值引用只能接收右值。

在模板和自动类型推导(auto)中,如果是模板参数,指定为 T&&,如果是自动类型推导,指定为 auto&&,这两种情况下,&& 被称作“未定的引用类型”。如果接收左值,那经过引用折叠就是一个左值,如果接收右值,那经过引用折叠就是一个右值。

(普通类型的&&没有推导,也就没有引用折叠现象)

另外 const T&& 表示一个右值引用,不是未定引用类型。

template<typename T>
void fun(T&& param)
{

}
int main()
{
    fun(10); //对于 f(10) 来说传入的实参 10 是右值,因此 T&& 表示右值引用
    int x = 1;
    fun(x);//对于 f(x) 来说传入的实参是 x 是左值,因此 T&& 表示左值引用
    return 0;
}

一些推导例子

int&& a1 = 1;        //右值            推导为            右值引用
auto&& bb = a1;        //右值引用(具名化后当成左值使用)    推导为        左值引用
auto&& bb1 = 2;        //右值            推导为            右值引用

int a2 = 1;
int& a3 = a2;        //左值            推导为            左值引用
auto&& cc = a3;        //左值引用        推导为            左值引用
auto&& cc1 = a2;        //左值            推导为            左值引用

const int& s1 = 1;      //常量左值引用
const int&& s2 = 1;     //常量右值引用
auto&& dd = s1;        //常量左值引用        推导为            左值引用
auto&& ee = s2;        //常量右值引用        推导为            左值引用

return 0;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凛_Lin~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值