IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++11新特性 : 右值引用 & 移动语义 & 完美转发 -> 正文阅读

[C++知识库]C++11新特性 : 右值引用 & 移动语义 & 完美转发

右值引用

什么是左值和右值

左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象。

区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值。所有的具名变量或对象都是左值,而右值不具名。

在C++11中,右值由两个概念组成:

  1. 纯右值。比如非引用返回的临时变量、运算表达式产生的临时变量、原是字面量(10、20、‘a’)、和lambda表达式等。
  2. 将亡值。将亡值是C++11新增的,与右值引用相关的表达式,比如将要被移动的对象、T&&函数返回值、std::move返回值和转换为T&&的类型的转换函数的返回值。

因此我们可以总结:C++11中所有的值必属于左值、将亡值、纯右值之一,其中将亡值、纯右值都属于右值。
同时C++11引入了新的类型,右值引用,标记为T &&。

对比左值、右值

右值引用就是对一个右值进行引用的类型。因为右值没有名字,我们只能通过引用的方式找到它。

强调一下:无论声明左值引用还是右值引用都必须立即进行初始化,因为引用类型本身并不拥有所绑定对象的内存,只是该对象的一个别名。通过右值引用的声明,该右值又“重获新生”,其生命周期与右值引用类型变量的生命周期一样,只要该变量还活着,该右值临时量将会一直存活下去。

universal references 未定的引用类型


这个例子可以看出,param有时是左值,有时是右值,因为在上面的例子中有&&。

未定引用类型(universal references),必须被初始化,它是左值还是右值取决于它的初始化,如果&&被一个左值初始化,它就是一个左值;如果他被一个右值初始化,它就是一个右值。

只有 T &&这样的引用才是未定的引用类型,其他单反具体一点的形式如const T &&Test<T> &&就都是右值引用。

移动语义

如果希望把一个左值赋值给一个右值引用该怎么做呢?这里引入移动语义std::move()

int a = 10; //a是左值
//int &&b = a; error
int &&b = std::move(a);

std::move可以将一个左值转换成右值

配合右值引用避免深拷贝

move是将对象的状态所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。
在这里插入图片描述
move实际上并不能移动任何东西,**它唯一的功能是将一个左值强制转换为一个右值引用,**使我们可以通过右值引用使用该值,以用于移动语义。强制转换为右值的目的是为了方便实现移动构造

这种move语义是很有用的,比如一个对象中有一些指针资源或者动态数组,在对象的赋值或者拷贝时就不需要拷贝这些资源了。

forward完美转发

现存在一个函数

Template<class T>
void func(T &&val);

根据前面所描述的,这种引用类型既可以对左值引用,亦可以对右值引用。

但要注意,引用以后,这个val值它本质上是一个左值!
看下面例子

int &&a = 10;
int &&b = a; //error

注意这里,a是一个右值引用,但其本身a也有内存名字,所以a本身是一个左值,再用右值引用引用a这是不对的。

因此我们有了std::forward()完美转发,这种T &&val中的val是左值,但如果我们用std::forward (val),就会按照参数原来的类型转发;

int &&a = 10;
int &&b = std::forward<int>(a);

这样是正确的!

大家分析一下下面的打印结果稍作巩固:

template <class T>
void Print(T &t)
{
    cout << "L" << t << endl;
}
template <class T>
void Print(T &&t)
{
    cout << "R" << t << endl;
}
template <class T>
void func(T &&t)
{
    Print(t);
    Print(std::move(t));
    Print(std::forward<T>(t));
}
int main()
{
    func(1);
    int x = 10;
    int y=20;
    func(x);
    func(std::forward<int>(y));
    return 0;
}

运行结果如下
L1 R1 R1
L10 R10 L10
L20 R20 R20

解释:
func(1) :由于1是右值,所以未定的引用类型T&&v被一个右值初始化后变成了一个右值引用,但是在func()函数体内部,调用PrintT(v) 时,v又变成了一个左值(因为在std::forward里它已经变成了一个具名的变量,所以它是一个左值),因此,示例测试结果第一个PrintT被调用,打印出“L1"
调用PrintT(std::forward(v))时,由于std::forward会按参数原来的类型转发,因此,它还是一个右值(这里已经发生了类型推导,所以这里的T&&不是一个未定的引用类型,会调用void PrintT(T&&t)函数打印 “R1”.调用PrintT(std::move(v))是将v变成一个右值(v本身也是右值),因此,它将输出”R1"
func(x)未定的引用类型T&&v被一个左值初始化后变成了一个左值引用,因此,在调用PrintT(std::forward(v))时它会被转发到void PrintT(T&t).

收工!!

参考文献

图论C11中的新特性 . 图论教育

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-23 16:27:24  更:2021-08-23 16:30:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 6:04:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计