c++左值,左值引用,右值,右值引用理解

   日期:2020-10-30     浏览:97    评论:0    
核心提示:首先看一个函数声明:void func(type &&rval);其中 type可以是任意类型(并非函数模板参数)。需要注意的是,此函数只能接收一种实参“类型”:右值,注意此类型指的是左值,左值引用,右值,右值引用这四种之一。以下我们暂时假定type为std::string。这种规定意味着我们只能通过这样:func(std::string("hello"));//手动构造一个右值传入或者这样:std::string s("hello");func(std::move(s));

首先看一个函数声明:
void func(type &&rval);
其中 type可以是任意类型(并非函数模板参数)。需要注意的是,此函数只能接收一种实参“类型”:右值,注意此类型指的是左值,左值引用,右值,右值引用这四种之一。

以下我们暂时假定typestd::string
这种规定意味着我们只能通过这样:
func(std::string("hello"));//手动构造一个右值传入
或者这样:

std::string s("hello");
func(std::move(s));//使用std::move()强转为右值,注意,是强转为右值!!!

以及这样:

std::string s("world");
func(static_cast<string &&>(s));		

来调用此函数。

可能有人会产生疑问:既然func函数函数形参是std::string&&类型,那么我传入一个相应的右值引用类型不可以吗?
即:

std::string &&rs = std::string("hello world");
func(ss);//error

这似乎与我们的直觉相违背,错误产生的原因有两点:

  • 右值引用本身是左值
  • 右值引用只能绑定到右值
    至于为什么,因为标准就是这么规定的。。。。

而与右值引用相对应的左值引用就没有太多限制,当我们声明一个具有左值引用形参的函数时:
void fun(std::string &s);

//显然其可以接受一个左值:
std::string s_("hehe");
fun(s_);
//同时也可以接受一个左值引用:


fun(ls);
//甚至可以绑定到一个右值引用:
std::string && rs = std::("haha");
fun(rs);

当然,当函数声明为void fun(const std::string &s)时,亦可以传入一个右值给此函数:

fun(std::string("fuck"));//ok

我们知道,函数传参规则和变量初始化规则相同,所以上述三种情况可以写成:

std::string s("fucccccck");
std::string &lval_1 = s;//左值引用绑定到左值
std::string &lval_2 = lval_1;//左值引用绑定到同类型引用所绑定的左值上
std::string && rval = std::string("ff");
std::string &lval_3 = rval;//情况3

这里情况3将一个右值引用初始化给一个左值引用,那lval_3到底绑定到谁呢?答案是左值引用会绑定到右值引用所绑定的右值对象上

我们知道非const引用不能绑定到右值对象上:

std::string &lref = std::string("haha");//error

但是以下方式却能间接绑定到右值:

std::string &&rref = std::string("hehehe");
std::string &lref = rref;
std::cout << lref << std::endl;//"hehehe"
lref[0] = 'p';
std::cout << lref << std::endl;//"pehehe"

所以对于初始化一个左值引用,不管是用一个左值,还是一个同类型的左值引用,或者一个右值引用,最终这个左值引用都会绑定到一个对象上。

接下来看一下std::move()和std::forward()函数。

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::type &&>(t);
}

让我比较产生疑惑的是move()函数的返回值,当我们传一个左值给move():

std::string s("he");
std::move(s);

函数模板move()的类型参数T会被推断为std::string &,即move()函数会变成这个样子:

typename remove_reference<std::string &>::type&& move(std::string& &&t)
{
	return static_cast<typename remove_reference<std::string &>::type &&>(t);
}

typename remove_reference<T>::type的作用就是将类型T中的引用去除,还原出原生类型。再通过引用折叠,所以最终move()函数会被实例化为下面这样:

std::string&& move(std::string &t)
{
	return static_cast<std::string &&>(t);
}

看似move()函数返回一个右值引用类型,但其实返回的是一个右值,或者说是将左值引用t所绑定的右值返回。文章开头提到的static_cast<std::string &&>效果也是一样。

所以,现在我们知道,对于一个函数,如果其声明为:

retType func(ParaType && t);

则说明该函数只接受右值参数(可以从move()函数和static_cast获得),那什么情况下需要将函数声明为此类型呢?我们知道右值对象是即将要消亡的对象,当函数接受此种参数即是在告诉函数:调用此函数之后的代码不会再使用此对象(或者说不会再使用该对象拥有的资源),所以该函数有权力、责任去管理利用所传进来的右值对象拥有的资源。

那如果是没有拥有任何资源的右值对象被传进来会怎样呢?

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服