std::move的学习笔记

目录

1. 前言

2.定义

3. 从左值和右值说起

4. 深入一点,说说右值引用

5. 标准库函数move登场

6. 解释一下上面那个难懂的代码吧

7. 参考资料


1. 前言

    在学习foolgo围棋开源代码的过程中,在ChainSet类的GetPiecesOfChain方法的时候,在获取容器v之后,返回对象时出现了以下代码

return move(v);

查了以下,原来move函数是c++新标准的东西,并非业务逻辑的函数。所以,需要查考资料学习这一新知识点。

2.定义

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

这段代码很难理解,要想理解以上代码,学习以下概念。

3. 从左值和右值说起

下面是摘自《C++ primer》第五版的一些概念

c++的表达式要不然是右值,要不然是左值。这两个名词是从c语言继承过来的:左值可以位于赋值语句的左边,右值则不能。

上述是左值和右值的最直观的认识。其实书中有一句话,对于认识左值和右值很有帮助。

当一个对象被用作右值的时候,用的是对象的值(内容); 当对象被用作右值的时候,用的是对象的身份(在内存中的位置)

根据上述所说,一个变量对象有两种属性,左值属性(使用其地址)和右值属性(使用其值)。举个简单的例子。

int a;
int b = 1;
a = b;   //怎么理解其左值/右值属性呢?

上述代码中,a和b都是变量,其地址是编译器自动生成的,a的值未知,b的值是1。我们都知道a = b 之后,a的值从未知变为1。从计算机的角度理解,从内存中b的地址上把值”1“(b的右值属性)取出来,然后放在内存中a的地址处(a的左值属性)。

4. 深入一点,说说右值引用

我们通过&&    而不是&来获得右值引用。如我们将要看到的,右值引用有一个重要的性质——只能绑定到一个将要销毁的对象。因此,我们可以自由的地将一个右值引用的资源“移动”另外一个对象中。

为了明白上面的话,我们最好用代码案列说明。

int a = 42;
int &b = a;

以上代码是普通的引用,即:给变量a起一个别名b, 变量a和引用b指向的是同一个内存地址,引用b绑定上了变量a。这其实是左值引用

int &&c = a * 3;

    上面引用c其实就是右值引用的一个案例。与左值引用不同的是,右值引用是用&&来标识右值引用的,而且,重要的一点是,表达式a * 3会创建一个临时的对象,这个临时对象(内存中有一个未标识的内存地址)在下一行代码运行的时候就会被销毁,存在的时间很“短”。右值引用就是用来标识这个临时对象。我们将可以被右值引用标识的临时对象的类型称为右值引用类型

int &&d = a; //错误代码

上述右值引用的代码是错误的,原因很简单,因为右值引用的对象a是一个变量,它不是一个临时的对象,它是一个被变量a标识过的一个内存位置,在右值引用之后紧挨的一行代码中,如果出现a,则是可以找到这个对象的。总而言之,右值引用的对象不能是被变量标识过的。所以有一个结论:

左值持久,右值短暂。

5. 标准库函数move登场

虽然不能将右值引用绑定在一个左值上,但我们可以显示地将一个左值转换为对应的右值引用类型。我们还可以通过调用一个名为move的新标准库函数来获得绑定到左值的右值引用,此函数定义在头文件utility中。

    这段话给出int &&d = a的错误代码的一个解决的方案,就是使用move函数将a变量转换成相应的右值引用类型。所以,下面的代码是对的。

int &&d = std::move(a); //ok

    move函数的作用就是告诉编译器我们有一个左值,但是我们希望可以像一个右值一样处理它。那么move函数的使用场景是怎么样的呢?为什么会使用move函数呢?这就要牵涉到移动对象的概念了。c++primer书中记载的已经很通俗易懂了,如下:

    在很多情况下都会发生对象拷贝,但在某些情况下,对象拷贝后就立即被销毁了,在这样的情况下,移动而非拷贝会大幅多提升性能。

    也就是说,拷贝对象的情况经常发生,“=”号的使用就使用了赋值拷贝,当然还有其他类型的拷贝,具体可以百度。拷贝就是,将数据从A内存地址copy到B内存地址上。如果两个内存地址在以后都要被使用,那么就不需要move函数。如果,其中B地址是没用的,那么,我们就不需要拷贝,只需要将B地址上的数据绑定到右值引用&&C即可,这样我们使用&&C就能找到B地址上的数值了。省略了拷贝所花费的时间和空间成本,这样就可以提高程序性能了。一句话,使用move函数与否,不会影响程序功能,但正确地使用move函数可以提升计算的程序性能。

6. 解释一下上面那个难懂的代码吧

拆分下面代码

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

    这是std::move的定义,首先它是一个模板方法,返回值是

constexpr typename std::remove_reference<_Tp>::type&&

    std::remove_reference<_Tp>是一个类,前缀关键字typename表明type是类中定义的变量。type&&表明是右值引用。

    std::move的参数拆分如下:

_Tp&& __t

通过引用折叠,此参数可以与类型的实参匹配,既可以给move传递一个左值,也可以传递给它一个右值。

未完待续。。。

且看下次补充

7. 参考资料

  1. 《C++ primer》第五版
  2. 知无涯之C++ typename的起源与用法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青草地溪水旁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值