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++]引用折叠规则,std::move如何工作 -> 正文阅读

[C++知识库][C++]引用折叠规则,std::move如何工作

引用折叠

我们知道,将一个左值传递给右值引用参数是不合法的,因为通常右值引用不能绑定到左值上,但是有两个例外。

  1. 右值引用指向模板类型参数,如 T&&,这种引用也叫做万能引用,因为它既可以被传入左值,也可以被传入右值。
template <typename T>
void f(T&& t) {}

当一个左值被传递给 T&& 参数时,如 int if(i) 进行调用,T 会被推断为 int&,而不是 int,这样 f 的参数就变成了 int& && 好像是一个引用的引用,这应该是不允许的啊。

事实上,我们确实不能直接定义一个引用的引用,但是通过模板参数间接定义是可以的。

  1. 如果定义出一个引用的引用,则这些引用会形成 折叠

折叠规则

对于一个给定的类型 X

  • X& &, X& &&, X&& & 会被折叠为 X&
  • X&& && 会被折叠为 X&&

只有右值引用的右值引用折叠后还是右值引用,其他只要沾上左值引用,最后都会被折叠为左值引用。

注意:引用折叠只能用于间接创建的引用的引用,如类型别名,或模板参数,auto。

所以,T&&参数,如果传入左值,会被折叠成 int&,传入右值,则会被折叠成 int&&,如果在 f 函数体内部需要将参数 t 传给其他具有万能引用的函数,那么由于右值引用和左值引用本身都是左值,传入到其他函数后会都被折叠成左值引用类型,如果要保持其引用类型不变,则需要进行完美转发。

move如何工作

我们常用 std::move 函数来将一个左值转换成右值,使用 move,即告诉编译器,我们希望将一个左值像一个右值一样处理它。

事实上,move 可以被传入任意类型的值,因为它是一个有万能引用的函数模板。

标准库中 move 是这样定义的:

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::type&&>(t);
}

remove_reference 是一个类模板。(详细文档:remove_reference - C++ Reference (cplusplus.com)

使用它可以将类型的引用去除,remove_reference 有一个模板类型参数和一个名为 type 的类型成员,如果我们使用一个引用类型实例化 remove_reference,则 type 将表示去除引用后的类型。如 remove_reference<int&>::type 就是 int 类型。

注意,使用时必须在前面加上 typename 以告知编译器 type 是一个类型成员。因为此时编译器不知道 remove_reference<T> 是一个什么样的类型,因为它还没有被实例化,其内部的 type 可能是 static 数据成员(编译器默认认为),也可能是类型成员,无法确定。需要程序员显式地使用 typename 告知才能通过编译。

static_cast用于强制类型转换,具体介绍在我的上一篇博客:[C++](27)强制类型转换 static_cast reinterpret_cast const_cast dynamic_cast_世真的博客-CSDN博客

如下两个例子,分别将右值和左值传入 move

string s1("hi!"), s2;
s2 = std::move(string("bye!")); //传入右值
s2 = std::move(s1);	//传入左值

第一个赋值(传入右值):

  • 函数参数为 T&&T 可以非常合理地被推断为 string

  • remove_reference<string>::type 非引用类型转换后不变,还是 string

  • 所以最后 move 被实例化为:

    string&& move(string&& t)
    {
    	return static_cast<string&&>(t);
    }
    
  • t 本身就是右值引用,所以强制类型转换什么也不做,最后返回的还是右值引用。

第二个赋值(传入左值):

  • 万能引用,编译器会将 T 推断为 string&

  • 函数参数 T&& 变成 string& && ,会折叠为 string&

  • remove_reference<string&>::type 就是 string

  • 所以最后 move 被实例化为:

    string&& move(string& t)
    {
    	return static_cast<string&&>(t);
    }
    
  • 这样,左值引用正好被转换成了右值引用。

这里的核心在于 static_cast,它可以将左值转换到右值引用。我们知道右值引用不能绑定到左值,这其实只是不允许进行隐式类型转换,而使用 static_cast 进行显式地转换是允许的。

此时,如果用一个对象 s2 来接收 move 的返回值,由于返回的是右值引用,那么就会调用 s2 的移动赋值运算符,将 s1 的资源转移给s2。

虽然允许我们自己使用static_cast将左值转换到右值类型,但还是建议使用标准库的 std::move,更加方便易读。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:22:12  更:2022-10-08 20:25:33 
 
开发: 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年5日历 -2024/5/19 5:06:12-

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