跟我学C++中级篇—如何理解std::move

一、移动语义

在前面的文章中对std::move进行了反复的分析,并对其背后的移动语义也进行了多视角的对比分析。比如右值引用,万能引用和完美转发等等。
对于很多人来说,其实对std::move与移动语义及std::move背后的行为并不甚了解。本文将尝试从不同的角度对std::move进行分析说明。

二、常见问题

先对移动语义和std::move的常见的细节问题进行一下说明,然后再进行分析。常见的有:

  1. std::move就是移动语义
    这个肯定是不正确的,std::move主要是进行左、右值的转换,它会触发移动语义
  2. std::move会移动数据
    这是最常见的理解错误,std::move只会将资源所有权转移而非拷贝。就像前面说的,只是把指针反向指向了另外一个所有者
  3. std::move移动后原对象消失
    std::move移动后,原对象会进入一个有效但未指定状态,不应该再进行使用。简单理解成置空或消失并不准确
  4. 三五法则
    这是一个很容易忽视的问题,在C++11后,除了构造函数以外的五个函数,只要有任何一定出现都必须定义其它几个。不定义行不行,行。但存在着隐性的问题
  5. 在返回值中滥用std::move
    这点是没有把RVO和NRVO理解透彻或者说干脆忽视了它们。很多情况下,RVO更高效,而使用std::move则阻止了返回值优化
    使用std::move移动的细节问题其实有很多,上面只是常见的几种,有兴趣的话可以多自己总结一下,这样会更好的加深对std::move和移动语义的理解。

三、理解和分析std::move

在了解了std::move的问题后,其实就很容易理解了其背后的机制。std::move负责触发移动语义,而真正的移动语义会定义在相关的移动构造函数和移动赋值函数。它只是一个类型转换接口(右值转右值引用),主要是为了防止深拷贝引起的性能下降。
其本质是一个“static_cast<typename std::remove_reference::type&&>(t)”,它不适合于小对象(深拷贝没影响)使用。const和本身就是右值的对象也不适用。其最适合应用的场景不是STL中的容器内对象的操作,可以大幅的减少拷贝的次数。比如常见的emplace_back中,就使用包括移动语义在内的提升性能的方法。
C++标准中对移动后原对象,使用的描述是“valid but unspecified”,这也是在上面提到时说单纯的指其为空或消失是不正确的原因所在。
在返回值的情况下,由于RVO的存在,一般是不建议使用std::move的,它会阻止返回值优化。这是得不偿失的。但在某些特定场景下,如返回成员变量、明确无法RVO以及调用已经明确包含移动结果的函数返回值等等。

来看一个简单的示意的例子:

#include <iostream>
#include <vector>
#include <string>

int main() {
    std::vector<std::string> vecSrc = {"a", "aa", "aaa"};
    std::vector<std::string> vecDest = std::move(vecSrc);
    
    // 此时可以安全回收vecSrc的资源即vecSrc的析构函数会正常工作
    
    // 重新赋值
    vecSrc = {"b", "bb"};  // OK
    
    // 调用状态无关的成员函数
    std::cout << "vecSrc容量: " << vecSrc.capacity() << std::endl;   
    std::cout << "vecSrc是否为空: " << vecSrc.empty() << std::endl;   
    
    // 状态相关
    // std::cout << vecSrc[0]<<std::endl;  // 不确定,有风险
    //std::cout << vecSrc.size()<<std::endl;//不保证size()一定是0
    return 0;
}

四、总结

其实很多小细节只有在用到之后才可能体会到其的重要性。没有发生的事,对开发者来说往往只是一个故事,听听看看就罢了。这是人类的本能,无可厚非。但应用在技术上,还是尽量不要心存侥幸。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值