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 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 探索 std::move -> 正文阅读

[游戏开发]探索 std::move

C++里通常分为左右值,但标准里更细化

img

这里需要明确右值的概念:

纯右值 prvalue 是没有标识符、不可以取地址的表达式,一般也称之为“临时对象”。

最常见的情况有:

  • 返回非引用类型的表达式,如 x++、x + 1、make_shared(42)
  • 除字符串字面量之外的字面量,如 42、true

C++11 开始,C++ 语言里多了一种引用类型—右值引用。右值引用的形式是 T&&,比左值引用多一个 & 符号。

我们使用右值引用的目的是实现移动,而实现移动的意义是减少运行的开销。在使用容器类的情况下,移动更有意义。

以下是MSVC的std::move源代码:

template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable
    return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}

template <class _Ty>
using remove_reference_t = typename remove_reference<_Ty>::type;

template <class _Ty>
struct remove_reference {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty;
};

template <class _Ty>
struct remove_reference<_Ty&> {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty&;
};

template <class _Ty>
struct remove_reference<_Ty&&> {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty&&;
};

MinGW源代码:

  template<typename _Tp>
    _GLIBCXX_NODISCARD
    constexpr typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t) noexcept
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

  /// remove_reference
  template<typename _Tp>
    struct remove_reference
    { typedef _Tp   type; };

  template<typename _Tp>
    struct remove_reference<_Tp&>
    { typedef _Tp   type; };

  template<typename _Tp>
    struct remove_reference<_Tp&&>
    { typedef _Tp   type; };

为了初学者更方便阅读,简化版:

  template<typename _Tp>
    typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t)
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

  /// remove_reference
  template<typename _Tp>
    struct remove_reference
    { typedef _Tp   type; };

  template<typename _Tp>
    struct remove_reference<_Tp&>
    { typedef _Tp   type; };

  template<typename _Tp>
    struct remove_reference<_Tp&&>
    { typedef _Tp   type; };

这里会出现一个概念,引用坍缩(又称“引用折叠”)。

  • 对于 template foo(T&&) 这样的代码,如果传递过去的参数是左值,T 的推导结果是左值引用;
  • 如果传递过去的参数是右值,T 的推导结果是参数的类型本身。如果 T 是左值引用,那 T&& 的结果仍然是左值引用——即 type&,也就是&& 坍缩成了 type&。
  • 如果 T 是一个实际类型,那 T&& 的结果自然就是一个右值引用。

std::forwardstd::move 一样都是利用引用坍缩机制来实现。它可以实现目标的参数类型不知道,但我们仍然需要能够保持参数的值类别:左值的仍然是左值,右值的仍然是右值。

写个例子看看现象:

class MoveClass {
 public:
	MoveClass()
	{
		cout << "default constructor" << endl;
	}
    //拷贝构造
	MoveClass(const MoveClass& m)
	{
		cout << "copy constructor" << endl;
	}
    //移动构造
	MoveClass(MoveClass&& m)
	{
		cout << "move constructor" << endl;
	}

	MoveClass Get()
	{
		MoveClass tmp;
		//简单返回对象,一般有 NRVO
		return tmp;
	}

	MoveClass GetMove()
	{
		MoveClass tmp;
		//此时使用move 会禁止 NRVO。也就是,用了 std::move 反而妨碍了返回值优化。
		return std::move(tmp);
	}
};
//main.cpp
int main()
{
	MoveClass m;
	std::cout << "1-------------------" << std::endl;
	MoveClass m1(std::move(m));
	std::cout << "2-------------------" << std::endl;
	MoveClass m2(std::move(MoveClass()));

	std::cout << "3-------------------" << std::endl;
	auto r = m.Get();
	std::cout << "4-------------------" << std::endl;
	auto r1 = m.GetMove();

	return 0;
}

输出:

default constructor
1-------------------
move constructor
2-------------------
default constructor
move constructor
3-------------------
default constructor
4-------------------
default constructor
move constructor

m1如果是m1(m)那么会调用拷贝构造,但是这里用了std::move(m)调用了移动构造。

m2同理,先默认构造出一个临时对象,再移动构造。

r,r1解释如下:

在 C++11 之前,返回一个本地对象意味着这个对象会被拷贝,除非编译器发现可以做返回值 优化(named return value optimization,或 NRVO),能把对象直接构造到调用者的栈上。

从 C++11 开始,返回值优化仍可以发生,但在没有返回值优化的情况下,编译器将试图 把本地对象移动出去,而不是拷贝出去。这一行为不需要程序员手工用 std::move 进行干预 ——使用 std::move 对于移动行为没有帮助,反而会影响返回值优化。

//TODO:添加std::forward

值类别

The deal with C++14 xvalues
《现代 C++ 编程实战》 吴咏炜

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-29 12:26:54  更:2022-04-29 12:28:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/17 0:48:37-

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