【仿写C++中的move函数和forward函数】

仿写实现move函数

一、值的类型

在这里插入图片描述

1.左值

描述:能够取地址的值成为左值

int a = 10;
const int b = 15;
int *pa = &a;
const int *pb = &b;

2.纯右值

描述:赤裸裸的字面值 eg(false , 3 , 12.23等)

int a = 13;
int *p = &a;  //取a的地址
int *ptr = &13; //错误,13是一个字面量,无法取地址 

如字面常量 103, 12.23, nullptr;不可以取地址, 称为为纯右值。

3.将亡值

描述:表达式的运行或计算过程中所产生的临时量或临时对象,称之为将亡值;临时量有可能是字面值,也有可能是一个不具名的对象。

算术表达式(a+b …),逻辑表达式(a || b …),比较表达式(a != b …)取地址表达式(&b)等,产生的计算结果相当于字面量,实际存储在 cpu的寄存器中 ,因为计算结果是字面量,所以为纯右值。

如图 在这里插入图片描述

因为c++是面向对象的,所以 i++ 和 ++i 是不一样的, ++i 相当于 i = i + 1; 所以 不会产生临时变量, 而 i++不同,它相当于 将 i 拷贝一份当做副本,然后将原来的 i 进行加 1 操作,最后将 副本 的值返回。 副本是不具名的,所以是 将亡值,而 返回的值是 字面量 所以 i++ 为纯右值

不具名对象如图

在这里插入图片描述

编译后会报错 test.cpp(17): error C2102: “&”要求左值,。

因为 Int(13) 程序运行过程中所产生了不具名对象,将亡值。 不可以取地址, 所以&Int(13)错误 。

int fun()
{
    int value = 10;
    return value;
}
int main()
{
    int i = 0;
    int a = 1;
    i = a + b;
    i++;
    &b;
    a = fun(); //返回时在主栈帧中构造临时量,是将亡值,纯右值。
    return 0;
}

流程图 图 2.2

在这里插入图片描述

当fun函数return 时 ,其栈帧空间会被回收,此时先在主栈帧中创建一个将亡值对象(xvalue) 将返回的值赋给将亡值,回到主函数栈帧中会再将将亡值对象的值赋给 a.

二、引用

基本原则:

1.声明引用的时候必须初始化,且一旦绑定,不可把引用绑定到其他对象;即引用必须初始化,不能对引用重定义

2.对引用的一切操作,就相当于对原对象的操作

1.引用与函数重载

void fun(int& val)
{
	cout << "LeftRef" << endl;
}
void fun(const int & val)
{
    cout << "Const LeftRef" << endl;
}
void fun(int&& val)
{
	cout << "RightRef" << endl;
}
int main()
{
	int a = 10; 
	const int d = 15;
	int&& f = 12; //右值引用f是具名右值引用,编译器会将已命名的右值引用视为左值
	//int&& g = f;  // error,f为具名右值
	fun(a);  
	fun(d);
	fun(f);
    fun(13);
    return 0;
}

运行结果:

在这里插入图片描述

结论:

​ 1.int& a = 10; 右值引用 a 是具名右值引用。

​ 2.编译器会将已命名的右值引用视为左值,所以不能 int && b = a;

2.函数返回值

int func()
{
    int x = 10;
    cout<<"&x: "<<endl;
    return x;
}

int main()
{
	int a = func();
	//int& b = func(); // error; func返回值为右值.
	const int& c = func(); 
	cout << "&c: " << &c << endl;
	int&& d = func();
	cout << "&d: " << &d << endl;

	return 0;
}

运行结果:

在这里插入图片描述

结论:1.函数返回值为将亡值(右值)

​ 2.常性左值引用为万能引用,可以接受右值

3.自定义类

class Int  
{
	int val;
public:
	Int(int x = 0) :val(x)
	{
		cout << "Create Int: " << this << endl;
	}
	Int(const Int& it) :val(it.val)
	{
		cout << this<<" Copy Create Int: <= " << &it << endl;
	}
	Int(Int&& it) :val(it.val)
	{
		it.val = 0;
		cout << this<<" Move Create Int: <= " << &it << endl;
	}
	Int& operator=(const Int& it)
	{
		if (this == &it) return *this;
		val = it.val;
		cout << this << " = " << &it << endl;
		return *this;
	}
	Int& operator=(Int&& it)
	{
		if (this == &it) return *this;
		val = it.val;
		it.val = 0;
		cout << this << " <= " << &it << endl;
		return *this;
	}
	~Int() { cout << "Destroy Int: " << this << endl; } 
};

Int func(int x)
{
	Int tmp(x);
	return tmp;
}

int main()
{
	Int a = func(1);
	cout << "--------------------" << endl;
	Int x(0);
	cout << "--------------------" << endl;
	x = func(2);
	cout << "--------------------" << endl;
	//Int& b = func(3);
	const Int& c = func(4);
	cout << "--------------------" << endl;
	Int&& d = func(5);
	cout << "--------------------" << endl; 
	Int f(a);
	cout << "--------------------" << endl;
	return 0;
}

运行结果

在这里插入图片描述

结论:

1.通过右值引用,比之前少了一次移动构造和一次析构,原因在于右值引用绑定了右值,让临时右值的生
命周期延长了 <主栈帧里创建的不具名对象(将亡值)>

2.函数返回值构建过程和之前分析的一样( 图 2.2

三、std::move的实现

原理:

本质上是将左值强制转换为右值引用,调用对象的移动构造和移动赋值函数,实现对资源的转移。

优点:

当一个对象内部有较大的堆内存或者动态数组时,进行深拷贝会占用cpu资源,而浅拷贝释放资源时会造成对堆区进行重复释放导致非法访问。使用move()语义可以提高性能

使用范围:

move 对于拥有形如对内存、堆区等资源的成员的对象有效

1.未定义的引用类别 && (函数模板中)

template <class _Ty>
void fun(_Ty&&  x) //未定义的引用类型,它必须被初始化,它是左值还是右值引用,取决于它的初始化
{
    int z = 10;
    _Ty y = z;
}
int main()
{
    int a = 1;
    const int b = 2;
    int& c = a;
    const int& d = b;
    fun(10);  
    fun(a);
    fun(b);
    fun(c);
    fun(d);
    return 0;
}

fun(a),fun©: x 的类型为 int& ,y的类型为 int&;

fun(b),fun(d):x的类型为 const int& ,y的类型为const int&;

fun(10):x的类型为 int&& , y 的类型为 int,不具有引用;

结论:

  1. _Ty && 与左值,普通左值引用结和,_Ty为左值引用

  2. _Ty&& 与左值常引用结合,_Ty为左值常性引用

  3. _Ty&& 与右值结合时,_Ty只保留类型,不具有引用属性

  4. _Ty&& 不会破坏掉 对象的const属性

实现流程:

1.我们需用去除对象的引用属性
template <class _Ty>
struct my_remove_reference
{
    using type = _Ty;

};
template <class _Ty>
struct my_remove_reference<_Ty &>
{
    using type = _Ty;

};
template <class _Ty>
struct my_remove_reference<_Ty &&>
{
    using type = _Ty;
    
};
2.加入适配器,简化代码
template <class _Ty>
using my_remove_reference_t = typename my_remove_reference<_Ty>::type;
3.转换为右值
template <class _Ty>
my_remove_reference_t<_Ty> && my_move(_Ty &&x)
{
    return static_cast<my_remove_reference_t<_Ty>&&>(x);
}

典型错误:使用c语言中的强制类型转换

template <class _Ty>
my_remove_reference_t<_Ty> && my_move(_Ty &&x)
{ 
   return (my_remove_reference_t<_Ty>&&)x; //error,不能使用c语言中的强制类型转换
}

因为常性对象的资源是不能进行移动修改的,而强制类型转换会破坏这一平衡点,而c++的静态类型转换刚好不会去掉const属性。

四、实现forward函数

原理:

利用引用叠加,按照参数原来的值类型转发到另一个函数

先来看std::forward()函数示例

void print(int& val)
{
    cout<<"LReference"<<endl;
}
void print(const int& val)
{
    cout<<"const LReference"<<endl;
}
void print(int&& val)
{
    cout<<"RReference"<<endl;
}
 
 template<class _Ty>
 void fun(_Ty&& val)
 {
    print(std::forward<_Ty>(val));
 }

int main()
{
  int a = 10;
  const int b = 20;
  int& c = a;
  fun(a);
  fun(b);
  fun(c);
  fun(10);
  system("pause"); 
}

运行结果

在这里插入图片描述

引用叠加:

由于存在 (_Ty&&) 这种未定的引用类型,当它作为参数时,可能被一个左值引用或者右值引用的参数初始化,这时经过类型推导的 _Ty 和右值引用(&&)叠加会发生类型的变化,这种变化被称为引用折叠。

即 static_cast<_Ty &&> (val);

c++中规定:

  1. 所有的右值引用叠加到右值引用上仍然还是一个右值引用。

  2. **所有的其他引用类型之间的叠加都将变成左值引用。

设计forward的参数

  1. 需要接受所有类型的数据 -> 联想到万能引用

  2. 需要保留原来数据的类型 -> 联想到去除引用属性的模板

去除引用属性的模板代码在上文move()函数实现中

1.左值版

template <class _Ty>
_Ty&& my_forward(my_remove_reference_t<_Ty>& Arg)//_Ty保留数据原来的属性
{
    return static_cast<_Ty &&> (Arg);  //此处涉及引用折叠
}

2.右值版 (forward还应支持右值参数)

template <class _Ty>
_Ty&& my_forward(my_remove_reference_t<_Ty>&& Arg)
{
    return static_cast<_Ty &&> (Arg);  //此处涉及引用折叠
}

这样就可以实现和系统的forward相同的功能

测试代码

template <class _Ty>
struct my_remove_reference
{
    using type = _Ty;

};
template <class _Ty>
struct my_remove_reference<_Ty &>
{
    using type = _Ty;

};
template <class _Ty>
struct my_remove_reference<_Ty &&>
{
    using type = _Ty;

};
template <class _Ty>
using my_remove_reference_t = typename my_remove_reference<_Ty>::type;

template <class _Ty>
_Ty &&my_forward(my_remove_reference_t<_Ty> &_Arg)
{
    return static_cast<_Ty &&>(_Arg);
}
template <class _Ty>
_Ty &&my_forward(my_remove_reference_t<_Ty> &&_Arg)
{
    return static_cast<_Ty &&>(_Arg);
}
void print(int& val)
{
    cout<<"LReference"<<endl;
}
void print(const int& val)
{
    cout<<"const LReference"<<endl;
}
void print(int&& val)
{
    cout<<"RReference"<<endl;
}
 template<class _Ty>
 void fun(_Ty&& val)
 {
    print(my_forward<_Ty>(val));
 }

int main()
{
  int a = 10;
  const int b = 20;
  int& c = a;
  fun(a);
  fun(b);
  fun(c);
  fun(10);
  print(my_forward(10));//右值版
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/196472.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis缓存设计典型问题

目录 缓存穿透 缓存失效&#xff08;击穿&#xff09; 缓存雪崩 热点缓存key重建优化 缓存与数据库双写不一致 缓存穿透 缓存穿透是指查询一个根本不存在的数据&#xff0c; 缓存层和存储层都不会命中&#xff0c; 通常出于容错的考虑&#xff0c; 如果从存储层查不到数据…

kubenetes--kube-proxy实现负载均衡

一、Linux网络收包 要了解kube-proxy如何实现负载均衡&#xff0c;要先了解Linux网络收包机制&#xff0c;kube-proxy利用Linux的内核实现的负载均衡。 在TCP/IP网络分层模型里&#xff0c;整个协议栈被分成了物理层、链路层、网络层&#xff0c;传输层和应用层。物理层对应的…

ESP Multi-Room Music 方案:支持音频实时同步播放 实现音乐互联共享

项目背景 随着无线通信技术的发展&#xff0c;针对不同音频应用领域的无线音频产品正不断涌现。近日&#xff0c;乐鑫科技推出了基于 Wi-Fi 的多扬声器互联共享音乐通信协议——ESP Multi-Room Music 方案。该方案使用乐鑫自研的基于 Wi-Fi 局域网的音频同步播放技术&#xff…

基于SSM框架的合同服务管理系统设计与实现

基于SSM框架的合同服务管理系统的设计与实现 摘要&#xff1a;当今社会&#xff0c;各行各业都离不开计算机软件的推广&#xff0c;销售&#xff0c;运营各方面&#xff0c;有些中小型企业没有技术与能力开发和维护一款软件来运营&#xff0c;这时催生很多软件外包公司的产生。…

【Electron】上下键切换消息

需求&#xff1a; 如图&#xff0c;需要监听上下键切换消息 Electron 注册 全局快捷键【globalShortcut】监听 在focus注册 在blur 注销 如苹果系统在使用某个软件(focus)时 右上角会有应用标题 Electron 代码&#xff1a; win.on(focus, ()>{globalShortcut.register(U…

建筑木模板厂家批发

在建筑施工中&#xff0c;木模板是一种常见且重要的施工材料&#xff0c;用于搭建混凝土浇筑的支撑结构。选择合适的建筑木模板厂家进行批发&#xff0c;对于施工质量和效率至关重要。本文将介绍建筑木模板厂家批发的重要性&#xff0c;并推荐贵港市能强优品木业作为专业的建筑…

【数据结构复习之路】树和二叉树(严蔚敏版)万字详解主打基础

专栏&#xff1a;数据结构复习之路 复习完上面四章【线性表】【栈和队列】【串】【数组和广义表】&#xff0c;我们接着复习 树和二叉树&#xff0c;这篇文章我写的非常详细且通俗易懂&#xff0c;看完保证会带给你不一样的收获。如果对你有帮助&#xff0c;看在我这么辛苦整理…

阿里云语雀频繁崩溃,有什么文档管理工具是比较稳定的?

10月23 日14:00左右&#xff0c;蚂蚁集团旗下的在线文档编辑与协同工具语雀发生服务器故障&#xff0c;在线文档和官网都无法打开。直到当天晚上22:24&#xff0c;语雀服务才全部恢复正常。从故障发生到完全恢复正常&#xff0c;语雀整个宕机时间将近 8 小时&#xff0c;如此长…

深度学习毕设项目 基于生成对抗网络的照片上色动态算法设计与实现 - 深度学习 opencv python

文章目录 1 前言1 课题背景2 GAN(生成对抗网络)2.1 简介2.2 基本原理 3 DeOldify 框架4 First Order Motion Model 1 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&am…

PS最新磨皮软件Portraiture4.1.2

Portraiture是一款好用的PS磨皮滤镜插件&#xff0c;拥有磨皮美白的功能&#xff0c;操作也很简单&#xff0c;一键点击即可实现美白效果&#xff0c;软件还保留了人物的皮肤质感让照片看起来更加真实。portraiture体积小巧&#xff0c;不会占用过多的电脑内存哦。 内置了多种…

交流负载测试使用场景

交流负载测试是一种在特定环境下&#xff0c;对电力设备、汽车电子部件&#xff0c;工业自动化设备、网络设备、家电产品&#xff0c;航空航天设备以及医疗器械等产品进行测试的方法&#xff0c;该测试的目的是评估这些设备在实际运行条件下的性能和可靠性。 1电力设备测试 交…

基于SSM实现的叮当书城

一、系统架构 前端&#xff1a;jsp | jquery | layui 后端&#xff1a;spring | springmvc | mybatis 环境&#xff1a;jdk1.7以上 | mysql | maven 二、代码与数据库 三、功能介绍 01. 系统首页 02. 商品分类 03. 热销 04. 新品 05. 注册 06. 登录 07. 购物车 08. 后台-首页 …

HTTP协议,Web框架回顾

HTTP 请求协议详情 -请求首行---》请求方式&#xff0c;请求地址&#xff0c;请求协议版本 -请求头---》key:value形式 -referer&#xff1a;上一次访问的地址 -user-agenet&#xff1a;客户端类型 -name&#xff1a;lqz -cookie&…

c语言,输入整数n(行数,本例为4),按照如下规则打印数字图片 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16

c语言&#xff0c;输入整数n(行数&#xff0c;本例为4&#xff09;&#xff0c;按照如下规则打印数字图片 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 以下是使用C语言编写的程序&#xff0c;根据输入的行数打印数字图片的规则&#xff1a; #include <stdio.h>int main() …

lack——主页前后端开发优化(精华:java多线程实现数据插入)

lack——主页前后端开发优化 前端开发主页 最容易的方式&#xff1a;list列表<template><van-cardv-for"user in props.userList":desc"user.profile":title"${user.username} (${user.planetCode})":thumb"user.avatarUrl"…

静态方法和属性的经典使用-单例设计模式

单例设计模式 一、设计模式二、单例模式1、饿汉式2、懒汉式3、区别 单例设计模式是静态方法和属性的经典使用。 一、设计模式 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱&#xff0c;不同的棋局&…

基于隐马尔可夫模型的一种流水印

文章信息 论文题目&#xff1a;An Invisible Flow Watermarking for Traffic Tracking: A Hidden Markov Model Approach 期刊&#xff08;会议&#xff09;&#xff1a;ICC 2019 - 2019 IEEE International Conference on Communications (ICC) 时间&#xff1a;2019 级别&am…

linux调用github代码文件

一&#xff0c;建库 默认建好库 默认linux已安装好git 二&#xff0c;生成SSH公钥 1&#xff0c;选这 2进教程&#xff0c;看怎么生成公钥的 根据下图教程&#xff0c;得到key&#xff0c;添加到github中&#xff01;具体教程&#xff0c;进github看 三。调用 gitssh链…

深度学习实战61-基于知识图谱与BiLSTM网络实现疾病相关智能问答系统,并支持数据扩展

大家好,我是微学AI,今天给大家介绍一下深度学习实战61-深度学习在医疗领域的应用:疾病相关智能问答系统,并支持数据扩展。本文将详细介绍如何使用Py2neo这个Python库来构建一个医疗领域知识图谱,并将数据导入Neo4j图数据库。我们将提供一些医疗领域的数据样例,并展示如何…

解决electron-build打包后运行app报错:cannot find module xxx

现象&#xff1a; 关于这个问题查了很多资料&#xff0c;也问了chatgpt都没有找到答案。 最后只能靠自己了。 于是冷静下来回想一下细节。突然发现了一个特别点。 eletron-builder打包时&#xff0c;强制要求eletron-builder和eletron必须都放在devDependencies 否则&#…