【C++】右值引用

右值的概念

左值、右值通常不是通过一个严谨的定义而为人所知的,大多数时候左右值的定义与其判别方法是一体的。一个最为典型的判别方法就是,在赋值表达式中,出现在等号左边的就是“左值”,而在等号右边的,则称为“右值”
更为细致地,在C++11中,右值是由两个概念构成的,一个是将亡值(xvalue,expiring Value),另一个则是纯右值(prvalue,Pure Rvalue)。
纯右值是C++98标准中右值的概念,用于辨识临时变量和一些不跟对象关联的值

  • 非引用返回的函数返回的临时变量值
  • 运算表达式
  • 类型转换函数的返回值
  • lambda表达式

将亡值则是C++11新增的跟右值引用相关的表达式,通常是将要被移动的对象

  • 返回右值引用T&&的函数返回值
  • std∶move的返回值
  • 可以标识函数、对象的值

右值引用

T && a = ReturnRvalue ();

ReturnRvalue 函数返回的右值在表达式语句结束后,其生命也就终结了,而通过右值引用的声明,该右值又"重获新生",其生命期将与右值引用类型变量 a的生命期一样。只要 a还"活着",该右值临时量将会一直"存活"下去。

相比于以下语句的声明方式∶

T b = ReturnRvalue();

通过右值引用的方式,少一次对象的析构以及一次对象的构造。a是右值引用直接绑定ReturnRvalue的返回值,b由临时值够成,临时值会经历一次构造和析构。

右值引用是不能够绑定到任何的左值的
C++98左值引用不能绑定右值,常量左值引用可以接受左值、常量左值、右值初始化

在这里插入图片描述

&& 的特性

C++ 中,并不是所有情况下 && 都代表是一个右值引用,具体的场景体现在模板和自动类型推导中

  • 在函数模板中使用 && 需要通过传入的实参来确定参数 param 的实际类型。

    template<typename T>
    void f(T&& param);
    void f1(const T&& param);
    
    //对于 f(10) 来说传入的实参 10 是右值,因此 T&& 表示右值引用
    f(10); 	
    int x = 10;
    
    //对于 f(x) 来说传入的实参是 x 是左值,因此 T&& 表示左值引用
    f(x); 
    
    //f1(x) 的参数是 const T&& 不是未定引用类型,不需要推导,本身就表示一个右值引用
    f1(x);
    
    
  • 如果是模板参数,需要指定为 T&&,如果是自动类型推导,需要指定为 auto &&,在这两种场景下 && 被称作未定的引用类型。

    int main()
    {
        int x = 520, y = 1314;
        //auto&& 表示一个整形的左值引用
        auto&& v1 = x;
        //auto&& 表示一个整形的右值引用
        auto&& v2 = 250;
        //decltype(x)&& 等价于 int&& 是一个右值引用不是未定引用类型,y 是一个左值,不能使用左值初始化一个右值引用类型。
        decltype(x)&& v3 = y;   // error
        cout << "v1: " << v1 << ", v2: " << v2 << endl;
        return 0;
    };
    
    

移动语义

在 C++11中,拷贝/移动构造函数实际上有以下3个版本∶

T Object(T &)
T Object(const T &)
T Object(T &&)
  • 常量左值引用的版本是一个拷贝构造版本
  • 右值引用版本是一个移动构造版本
  • 通常情况下,如果需要移动语义,程序员必须自定义移动构造函数

在这里插入图片描述
在这里插入图片描述

完美转发

完美转发(perfect forwarding),是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。

template <typename T>
void IamForwording (T t) {  IrunCodeActually(t); }

IrunCodeActually是执行代码的目标函数,希望转发函数将参数按照传入 lamforwarding时的类型传递。传入lamForwording 的是右值对象,IrunCodeActually就能获得右值对象)。

在上面例子中,使用了最基本类型进行转发,该方法会导致参数在传给 IrunCodeActually 之前就产生了一次额外的临时对象拷贝。因此这样的转发只能说是正确的转发,但谈不上完美。

C++11是如何解决完美转发的问题

C++11是通过引入一条所谓**"引用折叠”(reference collapsing)** 的新语言规则,并结合新的模板推导规则来完成完美转发

typedef const int T;
typedef T& TR; 
TR& v = 1; // 该声明在C++98中会导致编译错误

在C++11中,一旦出现了这样的表达式,就会发生引用折叠,即将复杂的未知表达式折叠为已知的简单表达式

在这里插入图片描述
一旦定义中出现了左值引用引用折叠总是优先将其折叠为左值引用
转发函数的实参是类型X的一个左值引用,那么模板参数被推导为X&类型
转发函数的实参是类型X的一个右值引用,那么模板参数被推导为X&&类型

template <typename T>
void IamForwording(T && t) { 
	IruncodeActually(static_cast<T &&>(t));
}

这里的static_cast是留给传递右值用的

void IamForwording(X& && t) {
	IruncodeActually(static_cast<X& &&>(t)); 
}

//应用上引用折叠规则,就是 -->>

void IamForwording(X& t) {
	IrunCodeActually(static_cast<X&>(t));
}

调用转发函数时传入了一个X类型的右值引用的话,我们的转发函数将被实例化为:

void IamForwording(X&& && t) { 
	IrunCodeActually(static_cast<X&& &&>(t));
}

//应用上引用折叠规则,就是 -->>

void IamForwording(X&& t) {
	IrunCodeActually(static_cast<X&&>(t));
}

static_cast的重要性: 将传入参数转换为右值。std∶move通常就是一个static_cast。
在C++11中,用于完美转发的函数叫做 forward

template <typename T>
void IamForwording(T && t) {
	IrunCodeActually(forward(t));
}

【实现完美转发的例子】

#include <iostream>
using namespace std;
template <typename T,typename U>
void PerfectForward (T &&t,U& Func){
	cout << t <<"\tforwarded..."<< endl;
	Func (forward<T>(t));
}
void RunCode (double && m) {}
void RunHome (double && h) {}
void RunComp (double && c) {}
int main(){ 
	PerfectForward(1.5,RunComp); //1.5 forwarded...
	PerfectForward(8,RunCode);// 8 forwarded... 
	PerfectForward (1.5,RunHome);// 1.5 forwarded...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++11中,右值引用的作用之一是实现移动语义,即对象的资源所有权的转移。在C++11之前,移动语义的缺失是C++所面临的一个问题。右值引用也可以看作是一块空间的别名,只能引用右值。通过使用右值引用,我们可以对右值进行引用,并且可以实现对移动语义的支持。右值引用的语法是在类型后面加上两个&&。在函数返回值为临时变量的情况下,可以使用右值引用来接收该临时变量。另外,右值引用还可以引用经过move操作后的左值,通过使用move函数,可以改变左值的属性,使其变成右值。总之,右值引用C++中的作用主要是支持移动语义,提高程序的性能和效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [c++右值引用具体用法](https://download.csdn.net/download/weixin_38734492/14887141)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++11——右值引用](https://blog.csdn.net/weixin_57023347/article/details/120957689)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值