c++左值,左值引用,右值,右值引用理解
发布日期:2022-02-17 02:39:58 浏览次数:27 分类:技术文章

本文共 2344 字,大约阅读时间需要 7 分钟。

首先看一个函数声明:

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
(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 remove_reference
::type&& move(T&& t){ return static_cast
::type &&>(t);}

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

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

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

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

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

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

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

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

retType func(ParaType && t);

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

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

转载地址:https://blog.csdn.net/qq_39815320/article/details/109298728 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:linux服务器编程之同步异步
下一篇:如何批量实现远程视频监控?不改路由设置、一次操作全部搞定!

发表评论

最新留言

很好
[***.229.124.182]2024年03月26日 17时39分40秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

【面试篇】数据结构-哈希表 2019-04-26
【Leetcode刷题篇】leetcode88 合并两个有序数组 2019-04-26
【Leetcode刷题篇】剑指offer51 数组中的逆序对 2019-04-26
【Leetcode刷题篇】剑指offer55-平衡二叉树 2019-04-26
【Leetcode刷题篇】leetcode98 判断一棵树是否为二叉搜索树 2019-04-26
Java中arraylist和数组的相互转换 2019-04-26
【Leetcode刷题篇 】leetcode147 对链表进行插入排序 2019-04-26
【Leetcode刷题篇】leetcode148 排序链表 2021-06-29
【面试篇】Java中String、StringBuilder与StringBuffer的区别? 2021-06-29
【面试篇】Java对象的hashCode()相同,equals()一定为true吗? 2021-06-29
【面试篇】Java中static和final关键字的作用是什么? 2021-06-29
【面试篇】Java中接口和抽象类的区别是什么? 2021-06-29
【Java网络编程与IO流】Java中IO流分为几种?字符流、字节流、缓冲流、输入流、输出流、节点流、处理流 2021-06-29
【Java网络编程与IO流】Java中BIO、NIO、AIO的区别是什么? 2021-06-29
【Leetcode刷题篇】leetcode136 只出现一次的数字 2021-06-29
spring boot整合thymeleaf,支持JSP和HTML页面开发 2021-06-29
【Java网络编程与IO流】Spring boot整合SSE实现服务器实时推送流信息 2021-06-29
【Java网络编程与IO流】SpringBoot + WebSocket + Netty实现实时的服务器消息推送 2021-06-29
【Leetcode刷题篇】leetcode141 环形链表II 2021-06-29
【Leetcode刷题篇】leetcode160 相交链表 2021-06-29