当前位置: 首页 > news >正文

C++:C++11新特性---右值引用

文章目录

  • 初始化方式
  • 显示查看类型
  • initializer_list
  • decltype
  • 左值引用和右值引用
    • move
    • 左右值引用的场景
  • 万能引用和完美转发

本篇总结C++11新特性

初始化方式

C++11对参数列表的初始化有了更明确的定义,可以这样进行定义

// 列表初始化
void test1()
{// 旧版本int x = 0;// 新版本int y{ 0 };int z = { 0 };int arr[]{ 10,20,30 };int* pa = new int[5]{ 1,2,3,4,5 };
}

在对类的赋值的时候,也可以这样进行赋值

struct t
{int a;int b;
};void test2()
{t* pt = new t[5]{ 1,2 };
}

显示查看类型

// 查看类型
void test3()
{int a = 1;cout << typeid(a).name() << endl;auto it = map<int, int>().begin();cout << typeid(it).name() << endl;
}

运行结果:

int
class std::_Tree_iterator<class std::_Tree_val<struct std::_Tree_simple_types<struct std::pair<int const ,int> > > >

initializer_list

这是什么呢?如何理解这个类型?先看一下在什么场景中会出现这个东西

void test4()
{auto lit = { 1,2,3,4 };cout << typeid(lit).name() << endl;
}

那么这个东西是干什么的呢?有什么用呢?

在这里插入图片描述
在C++11中,对于STL的各类容器的构造函数中,新增了这样的构造方式,有点类似于一个数组,它里面可以存储任意类型的数据,然后可以交给vector来实现构造,因此下面就要对{}进行一个对比

void test5()
{// 利用initializer_list进行初始化vector<int> v{ 1,2,3,4,5 };auto lit = { 1,2,3,4 };vector<int> vc(lit);// 调用参数初始化列表进行初始化int arr[]{ 1,2,3,4,5 };
}

这两种写法看似,但是实际上底层是完全不同的两种实现的方式

decltype

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret;cout << typeid(ret).name() << endl;
}void test6()
{const int x = 1;double y = 2.2;decltype(x * y) ret;decltype(&x) p;cout << typeid(ret).name() << endl;cout << typeid(p).name() << endl;F(1, 'a');
}

简单来说,就是可以把你要新定义的一个类型进行人为的定义,定义成一个你想让它变成的类型,使用场景也不算多,但是可以这样进行使用

左值引用和右值引用

首先要解决一个问题,什么是左值?

之前可能会说,左值就是等号左边的值,这肯定是不对的,左值是一个表示数据的表达式,可以获取它的地址,也可以进行赋值,通常来说它出现在赋值符号的左边,右值不能出现在赋值符号的左边,定义的时候,const修饰符修饰的变量不可以对它进行赋值,但是可以对它取地址,因此,可以说左值引用就是给左值的引用,给左值取别名

常见的左值

int* p = new int(0);
int b = 1;
const int c = 2;

因此我们说,可以对这些起一个别名

int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

常见的右值

10;
x + y;
fmin(x, y);

而对这些起别名就是右值引用

int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);

对于右值引用来说,可以理解为,右值本身是不能取地址的,但是给右值取别名之后,右值就会被存储到某些地方,此时就可以取到它的地址

左值引用只能引用左值,不能引用右值,如果想引用右值需要用const

int main()
{// 左值引用只能引用左值,不能引用右值。int a = 10;int& ra1 = a;  // ra为a的别名//int& ra2 = 10;  // 编译失败,因为10是右值// const左值引用既可引用左值,也可引用右值。const int& ra3 = 10;const int& ra4 = a;return 0;
}

move

右值只能引用右值,不能引用左值,但是右值可以引用move之后的左值

void test7()
{// 右值引用只能右值,不能引用左值。int&& r1 = 10;// error C2440: “初始化”: 无法从“int”转换为“int &&”// message : 无法将左值绑定到右值引用int a = 10;//int&& r2 = a;// 右值引用可以引用move以后的左值int&& r3 = std::move(a);
}

左右值引用的场景

那左右值引用能干啥呢?

左值引用

对比下面两种传参方式

void func(const string& str);
void func(string str);

从传参的效率上就不一样了,对于传引用来说,每次传参的代价是很低的,只需要把变量的地址给过去就可以了,但是对于传值来说就不一样了,传参的代价是相当高的,需要把原始的参数拷贝给新的值

也就是说,左值引用做参数减少了拷贝,提高效率的使用场景和价值

左值引用的缺陷

那左值引用有什么缺陷呢?

第一个是,当函数返回的对象是一个局部对象的时候,是不可以使用传引用返回的,因为这个变量出了作用域就被销毁了,因此不能使用传左值引用,只能进行传值返回,例如下面的场景

	mystring::string to_string(int x){mystring::string ret;while (x){int val = x % 10;x /= 10;ret += ('0' + val);}reverse(ret.begin(), ret.end());return ret;}

此时的string是一个局部变量,如果使用传引用返回会访问一个不存在的地址,这是不被允许的,但如果采用传值返回,又会导致增加了拷贝构造的次数,并且可能还是两次

在新的编译器中,函数得到的这个string在返回的时候,会直接赋给新的值,调用一次拷贝构造,而在旧一点的编译器中,从函数的返回值会先拷贝构造一次到一个临时常量,再从这个临时常量拷贝构造一次到外部定义的string中

而使用移动构造,可以解决这个问题,编译器会默认使用最匹配的参数进行调用,因此会优先使用移动构造

搭配move函数

move函数的作用,就是单纯的把左值转换成右值引用,由此来实现移动语义

那这样有什么用呢?该如何理解这个意思呢?

如果有下面的代码:

string s2(s1)

这样的语句代表的含义是,调用拷贝构造,这里的s1是一个左值

但是如果要是改成这样

string s2(move(s1))

这样就不一样了,把s1放move函数中,这样就可以把s1当成一个右值,而右值是可以调用移动构造的,这样就可以不用调用拷贝构造浪费空间,而是可以直接的把值置换到我们所需要的s2里面,但是这样其实是不好的,这样会导致,虽然确实把s2的值填充了,但是却把s1的值架空了

简单来说,移动构造就是把资源全部偷过来,把原来的资源都架空

再举一个例子:

int main()
{
list<string> lt;
string s1("1111");
// 这里调用的是拷贝构造
lt.push_back(s1);
// 下面调用都是移动构造
lt.push_back("2222");
lt.push_back(std::move(s1));
return 0;
}

如果只是简单的调用s1,那么s1会被当成是一个左值,而左值会调用的是拷贝构造,但是如果把它强制转换成右值,那么就会调用的是移动构造,很明显,移动构造的使用成本是要比拷贝构造低很多的

万能引用和完美转发

前面关于右值引用中和前面有比较大不同的一点就是出现了&&符号,如果把这个符号看成是右值引用的标识符,也是不对的,C++11在模板中也新增了关于&&符号,这个符号代表的是万能引用,而不是右值引用,简单来说就是,既能接收左值也能接收右值

模板的万能引用只是提供了一个可以接收左值和右值的能力,一般来说是不可以两个都接收的
实际的使用中,引用类型的作用会限制接收的类型,会变成左值,而如果想要在传递的过程中保持右值的属性,就需要用到万能引用和完美转发

下面做一个实验来验证功能

void check(int& t)
{cout << "左值引用" << endl;
}void check(int&& t)
{cout << "右值引用" << endl;
}void check(const int& t)
{cout << "const左值引用" << endl;
}void check(const int&& t)
{cout << "const右值引用" << endl;
}void test8()
{const int a = 1;const int& b = 1;int c = 0;check(a);check(b);check(c);check(1);check(move(a));check(move(b));
}

在这里插入图片描述

结果也是符合预期的,把上述代码进行适当更改

void check(int& t)
{cout << "左值引用" << endl;
}void check(int&& t)
{cout << "右值引用" << endl;
}void check(const int& t)
{cout << "const左值引用" << endl;
}void check(const int&& t)
{cout << "const右值引用" << endl;
}void func1(int t)
{check(t);
}void test8()
{const int a = 1;const int& b = 1;int c = 0;func1(move(a));
}

此时运行结果是左值引用,这是为什么?其原因是进入func1函数后,函数参数就从右值变成左值了,此时它的右值属性就会消失,因此出现了下面的用法:

template<class T>
void func1(T&& t)
{check(t);
}

此时运行结果是const左值引用,这说明它保持了const属性,但是依旧没有保持右值的属性,右值的属性依旧被退化成左值了

再进行改良

template<class T>
void func1(T&& t)
{check(forward<T>(t));
}

此时运行结果就是const右值引用了,而新增的这个forward其实就是完美转发,它可以保障原来的属性,把原来这个值的属性转发出去,严格意义来说保持的是左值和右值,而如果没有万能引用运行结果依旧会丢失const属性

相关文章:

C++:C++11新特性---右值引用

文章目录 初始化方式显示查看类型initializer_listdecltype左值引用和右值引用move左右值引用的场景 万能引用和完美转发 本篇总结C11新特性 初始化方式 C11对参数列表的初始化有了更明确的定义&#xff0c;可以这样进行定义 // 列表初始化 void test1() {// 旧版本int x 0…...

计算机人机界面

人机界面是指入与机器之间相互交流和影响的区域。人机界面包括对数据和信息的输入和输出方法&#xff0c;以及人们对机器的操作和控制。早期&#xff0c;人机交互界面是控制合&#xff0c;随后通过键盘进行操作&#xff0c;目前为鼠标和键盘操作&#xff0c;而智能手机采用触摸…...

【BUG合集】(一)①数据库存1/0,请求结果返回true和false;②sql查数据库能查,但mybatis查为空;③data64图片存储为异常;

前言 最近&#xff0c;在工作上接手的任务中&#xff0c;各种 bug 问题出现&#xff0c;在解决的同时也可以记录一下。因此&#xff0c;觉得可以出个记录 bug 合集。方便后来者碰到类似情况&#xff0c;可以作为一个参考进行解决。 文章题目就包含当前文章内容中所遇到的三个 b…...

python基础练习题库实验7

文章目录 题目1代码实验结果题目2代码实验结果题目3代码实验结果题目总结题目1 编写代码创建一个名为Staff的类和方法__init__,以按顺序初始化以下实例属性: -staff_number -first_name -last_name -email 代码 class Staff:def __init__(self, staff_number, first_name,…...

C语言你爱我么?(ZZULIOJ 1205:你爱我么?)

题目描述 LCY买个n束花准备送给她暗恋的女生&#xff0c;但是他不知道这个女生是否喜欢他。这时候一个算命先生告诉他让他查花瓣数&#xff0c;第一个花瓣表示"爱"&#xff0c;第二个花瓣表示"不爱"&#xff0c;第三个花瓣表示"爱"..... 为了使最…...

动手学深度学习(三)---Softmax回归

文章目录 一、理论知识1.图像分类数据集2.softmax回归的从零开始实现3.Softmax简洁实现 【相关总结】torch.sum()torch.argmax()isinstance():[python] softmax回归 一、理论知识 回归估计一个连续值分类预测一个离散类别 回归单连续数值输出自然区间R跟真实值的区别作为损失 …...

爬虫代理技术与构建本地代理池的实践

爬虫中代理的使用&#xff1a; 什么是代理 代理服务器 代理服务器的作用 就是用来转发请求和响应 在爬虫中为何需要使用代理&#xff1f; 隐藏真实IP地址&#xff1a;当进行爬取时&#xff0c;爬虫程序会发送大量的请求到目标网站。如果每个请求都使用相同的IP地址&#xff…...

token认证机制,基于JWT的Token认证机制实现,安全性的问题

文章目录 token认证机制几种常用的认证机制HTTP Basic AuthOAuthCookie AuthToken AuthToken Auth的优点 基于JWT的Token认证机制实现JWT的组成认证过程登录请求认证 对Token认证的五点认识JWT的JAVA实现 基于JWT的Token认证的安全问题确保验证过程的安全性如何防范XSS Attacks…...

什么是计算机病毒?

计算机病毒 1. 定义2. 计算机病毒的特点3. 计算机病毒的常见类型和攻击方式4. 如何防御计算机病毒 1. 定义 计算机病毒是计算机程序编制者在计算机程序中插入的破坏计算机功能或者破坏数据&#xff0c;影响计算机使用并且能够自我复制的一组计算机指令或程序代码。因其特点与生…...

【C++】哈希(位图、布隆过滤器)

一、哈希的应用&#xff08;位图和布隆过滤器&#xff09; 1、位图&#xff08;bitset&#xff09; &#xff08;1&#xff09;位图概念 【题目】 给 40亿 个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这 40亿 个数中。…...

LeetCode198.打家劫舍

打家劫舍和背包问题一样是一道非常经典的动态规划问题&#xff0c;只要做过几道动态规划的题&#xff0c;这道题简直就非常容易做出来。我应该花了10来分钟左右就写出来了&#xff0c;动态规划问题最重要的就是建立状态转移方程&#xff0c;就是说如何从上一个状态转移到下一个…...

Appium PO模式UI自动化测试框架——设计与实践

1. 目的 相信做过测试的同学都听说过自动化测试&#xff0c;而UI自动化无论何时对测试来说都是比较吸引人的存在。相较于接口自动化来说&#xff0c;它可以最大程度的模拟真实用户的日常操作与特定业务场景的模拟&#xff0c;那么存在即合理&#xff0c;自动化UI测试自然也是广…...

使用VUE3实现简单颜色盘,吸管组件,useEyeDropper和<input type=“color“ />的使用

1.使用vueuse中的useEyeDropper来实现滴管的功能和使用input中的type"color"属性来实现颜色盘 效果&#xff1a; 图标触发吸管 input触发颜色盘 组件代码部分 &#xff1a;<dropper> ---- vueuse使用 <template><div class"sRGBHexWrap fbc…...

matlab提取特征(医学图像)

乳腺肿瘤图片提取特征: %形态特征 %周长 面积 周长面积比 高度 宽度 纵横比 圆度 矩形度 伸长度 拟合椭圆长轴长 拟合椭圆短轴长 %拟合椭圆长轴与皮肤所夹锐角 最小外接凸多边形面积 最小外接凸多边形面积与肿瘤区面积比 %小叶树 叶指数 %纹理特征 %方差 熵 最小边差异 四个方…...

P4 C++ 条件与分支(if)

前言 今天我们来看看条件语句&#xff0c;换句话说&#xff0c;也就是 if 语句、if else 和 else if 等等这写语句。 我知道大家基本上已经非常了解 if 语句和所有 C 中的分支语句&#xff0c;但我还是鼓励你们继续看完这一讲&#xff0c;这里可能包含一些新东西。我们还会深入…...

django+drf+vue 简单系统搭建 (4) 用户权限

权限控制是web中的重要组成部分。与以往的博客系统不同&#xff0c;本次工具页面仅支持注册用户。 每个注册用户都能访问到工具页面&#xff0c;并且提交自己的task来选择具体的工具来处理自己提交的文件。每个注册用户都只能访问到自己提交的task&#xff0c;而管理员则可以查…...

stm32 计数模式

计数模式 但是对于通用定时器而言&#xff0c;计数器的计数模式不止向上计数这一种。上文基本定时器中计数器的计数模式都是向上计数的模式。 向上计数模式&#xff1a;计数器从0开始&#xff0c;向上自增&#xff0c;计到和自动重装寄存器的目标值相等时&#xff0c;计数器清…...

rss服务搭建记录

layout: post title: RSS subtitle: vps搭建RSS服务 date: 2023-11-27 author: Sprint#51264 header-img: img/post-bg-universe.jpg catalog: true tags: - 折腾 文章目录 引言RSShub-dockerRSS-radarFreshrssFluent reader获取fever api配置Fluent Reader同步 结语 引言 一个…...

GEE 23:基于GEE实现物种分布模型之随机森林法

基于GEE实现物种分布模型之随机森林法 1.物种分布数据2.研究区绘制3.预测因子选择 1.物种分布数据 根据研究目的和需要导入物种数据&#xff1a; // Load presence data var Data ee.FeatureCollection("users/************736/Distribution"); print(Original da…...

HCIE 01:基于前缀列表的BGP ORF功能

当运行BGP协议的某台设备上&#xff0c;针对入方向配置了基于ip-prefix的路由过滤&#xff0c;过滤了邻居发送的路由&#xff1b; 目前想&#xff0c;通过在peer关系的两端设备上都配置ORF功能&#xff0c;实现路由发送端只能送路由接收端过滤后的路由&#xff1b; ORF功能的说…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...