C++11:右值引用,实现移动语义和完美转发

news/2024/11/19 23:38:11/

目录

1、右值引用

2、移动语义(std::move)

3、完美转发(std::forward)


1、右值引用

右值引用(Rvalue reference)是C++11引入的一个新特性,它是一种新的引用类型,用于表示将要被移动的对象或临时对象。

(1)首先了解几个关键名词:

(1)左值:可以取地址的表达式,并且有名字;

(2)右值:不能取地址的表达式,且没有名字;

(3)纯右值:运算表达式产生的临时变量,不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式都是纯右值;

(4)将忘值:即将销毁的值;

(5)左值引用&:对左值进行引用的类型,等号右边的值必须可以取地址

(6)右值引用&&:对右值进行引用的类型,等号右边的值需要是右值,可以使用std::move函数强制把左值转换为右值。

(2)相关的代码展示:

int a = 1;  // a是左值
int b = a;  // a是左值,b是左值
int c = a + b;  // a和b是右值,c是左值
int&& rvalue_ref = 1;  // rvalue_ref是右值引用int &&d = a; // error, a是左值
int &&e = std::move(a); // ok

2、移动语义(std::move)

(1)移动语义含义:转移资源所有权,类似转让或者资源窃取,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用;理解深浅拷贝,直接把原来需要拷贝的内存易主

在C++11之前,我们拥有4个特殊成员函数,即构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。从C++11开始,我们多了2个特殊成员函数,即移动构造函数和移动赋值运算符。

在C++11之后,如果我们定义一个空类,除了之前的4个特殊成员函数,编译器还会为我们生成移动构造函数和移动赋值运算符: 

但是我们自定义一些函数时候,可能就不一定会自动生成移动构造函数和移动赋值运算符,相关的关系看下面的表:

(2)右值引用的主要作用是实现移动语义(Move Semantics),即在对象的拷贝或赋值操作中,将资源的所有权从一个对象转移到另一个对象,避免不必要的拷贝和内存分配。例如:

// 例子1
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = std::move(vec1);  // 使用右值引用实现移动// 例子2
std::vector<string> vecs;
...
std::vector<string> vecm = std::move(vecs); // 免//类的
int main() 
{A a(10);A b = a;A c = std::move(a); // 调用移动构造函数return 0;
}

例子1:在上面的代码中,使用std::move将vec1转换为右值引用,然后将其赋值给vec2。由于vec1已经成为右值,因此可以安全地将其资源(即动态分配的内存)转移给vec2,避免了不必要的拷贝和内存分配。

注意:移动语义仅针对于那些实现了移动构造函数的类的对象,对于那种基本类型int、float等没有任何优化作用,还是会拷贝,因为它们实现没有对应的移动构造函数。

还需要关注的重点在于我们需要把传入对象A的数据清除,不然就会产生多个对象共享同一份数据的问题。被转移数据的对象会处于"有效但未定义(valid but unspecified)"的状态。原本的指针要置空。

3、完美转发(std::forward)

(1)右值引用还可以用于实现完美转发(Perfect Forwarding),即在函数模板中将参数按原样转发给另一个函数。即是:转发函数实参是左值,那目标函数实参也是左值;(右值同理)

(2)使用 std::forward 的示例:

#include <iostream>
#include <utility>template<typename T>
void print(T&& arg) {std::cout << "Printing: " << std::forward<T>(arg) << std::endl;
}int main() {int a = 5;const int b = 10;print(a); // T is int&, arg is int&print(b); // T is const int&, arg is const int&print(15); // T is int, arg is int&&return 0;
}

在这个示例中,我们定义了一个名为 print 的模板函数,接受一个类型为 T 的参数 arg。在函数体内,我们通过 std::forward 将 arg 转发给 std::cout,保留其原始值类型和常量性质。

在 main 函数中,我们分别调用 print 函数,传递了一个 int 类型的变量 a,一个 const int 类型的变量 b,以及一个 int 字面量 15。当我们传递 a 和 b 时,T 的类型分别为 int& 和 const int&,因此 arg 的类型也分别为 int& 和 const int&。当我们传递 15 时,T 的类型为 int,arg 的类型为 int&&。

在每个调用中,我们使用 std::forward 将 arg 转发给 std::cout,以便正确地保留其原始类型和常量性质。例如,当 T 为 int& 时,std::forward<T>(arg) 将返回 arg 的左值引用,而当 T 为 int&& 时,std::forward<T>(arg) 将返回 arg 的右值引用。


http://www.ppmy.cn/news/225185.html

相关文章

MySQL安装流程 及 8.0与5.7区别

一、MySQL版本介绍 1、MySQL 8.0 窗口函数&#xff1a;MySQL 8.0版本支持窗口函数&#xff0c;这是数据分析工作中非常常用的一类函数。窗口函数可以让用户在单个查询中跨多个行检索数据&#xff0c;并在查询结果中对数据执行计算。隐藏索引&#xff1a;在MySQL 8.0版本中&am…

电动力学专题:电磁场规范不变性与规范自由度

对称性&#xff0c;不变性&#xff0c;相对性&#xff0c;协变形 在现代物理学中常常被认为具有相同的含义&#xff08;好拗口&#xff09; 规范与规范的自由度 保证电磁场物理量不改变的情况下&#xff0c;有多组势可供选择&#xff0c;而每组势可以称为一个规范 规范不变性…

Nginx:Rewrite

Nginx&#xff1a;Rewrite 一、常用的Nginx 正则表达式二、location2.1 location 大致可以分为三类2.2 location 常用的匹配规则2.3 location 优先级2.4 实际网站使用中&#xff0c;至少有三个匹配规则定义 三、rewrite3.1 rewrite功能3.2 rewrite跳转实现3.3 rewrite 执行顺序…

300亿收购芯片厂,佳能还能继续“感动常在”吗?

不知从何时起&#xff0c;数码相机开始失去了消费者的爱。 2020年&#xff0c;全球数码相机供货量同比上年大幅减少40.3%&#xff0c;降至885万部。受到疫情的限制&#xff0c;全球消费者的旅游出行次数减少&#xff0c;拖累了数码相机的销量。 随着疫情逐渐受控&#xff0c;…

西部之旅之------相机的选择

卡片机的选择 【经济条件好用户的选择】 莱卡M、索尼RX1 【小资之选】 富士X-PRO1、X-E1、X100 索尼NEX7、NEX6、NEX5、NEX3、RX100 适马DP系列&#xff08;色彩很特别&#xff09; 佳能G1X 松下GF系列 奥林巴斯EP系列、EPL系列 【两千至四千】 佳能G15为代表的G系列&#xff0…

Python的标识符命名规范

简单地理解&#xff0c;标识符就是一个名字&#xff0c;就好像我们每个人都有属于自己的名字&#xff0c;它的主要作用就是作为变量、函数、类、模块以及其他对象的名称。 Python 中标识符的命名不是随意的&#xff0c;而是要遵守一定的命令规则&#xff0c;比如说&#xff1a…

在虚拟机上安装MySQL和Hive

文章目录 零、学习目标一、Hive概述(一)Hive的SQL - HQL(二)数据库与数据仓库(三)Hive的适用场景二、下载、安装和配置MySQL(一)下载MySQL组件压缩包(二)将MySQL组件压缩包上传到虚拟机(三)删除系统自带的MariaDB1、查询mariadb2、删除mariadb(四)安装MySQL组件1…

r7与matlab是否兼容,R7-2700配什么主板好 AMD锐龙7 2700适配主板推荐

AMD第二代锐龙已经全面上市了&#xff0c;虽然Intel也发布了酷睿八代处理器进行竞争&#xff0c;但从市面表现来看&#xff0c;二代锐龙还是蛮有竞争力的。由于之前B450主板尚未上市&#xff0c;所以出现了二代锐龙需要搭配B350主板&#xff0c;那么R7-2700配什么主板好&#x…