听说还有人不懂右值和std::move()?

2023-05-16

1.左值和右值

左值是可以出现在等号左边的符号,当然它也可以出现在等号右边,例如int a等等。右值是能且只能出现在等号右边的符号,例如5,“abc”等等。右值细分为将亡值和纯右值,目前来看不必理会。判断一个值是否为左值有一个很粗暴的办法:是否能取地址。

2.左值引用和右值引用

对左值的引用即为左值引用,对右值的引用即为右值引用,例如下面的示例。左值只能对应左值引用,右值只能对应右值引用。

int a = 3;
int& left_ref = a;
int&& right_ref = 3;

//int& left_ref = 3; 报错
//int&& right_ref = a; 报错 

3.移动拷贝构造函数和移动拷贝赋值函数

看下面代码即可,可以想到参数为MyString& 和MyString&&是对函数的重载。所谓移动拷贝构造函数和移动拷贝赋值函数,只是用右引用来做参数。

#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

class MyString {
        private:
                char* data;
                size_t   len;
                void init_data(const char *s) {
                        this->len = strlen (s);
                        data = new char[this->len+1];
                        memcpy(this->data, s, this->len);
                        data[this->len] = '\0';
                }
        public:
                MyString() {
                        this->data = NULL;
                        this->len = 0;
                }

                MyString(const char* p) {
                        this->init_data(p);
                }

                MyString(const MyString& str) {
                        std::cout << "Copy Constructor ! source: " << str.data << std::endl;
                        this->len = str.len;
                        init_data(str.data);
                }

                MyString(MyString&& str) {
                        std::cout << "Move Constructor ! source: " << str.data << std::endl;
                        this->len = str.len;
                        this->data = str.data;
                        str.len = 0;
                        str.data = NULL;
                }
                MyString& operator=(const MyString& str) {
                        std::cout << "Copy Assignment ! source: " << str.data << std::endl;
                        if (this != &str) {
                                this->len = str.len;
                                init_data(str.data);
                        }
                        return *this;
                }
                MyString& operator=(const MyString&& str) {
                        std::cout << "Move Assignment ! source: " << str.data << std::endl;
                        if (this != &str) {
                                this->len = str.len;
                                init_data(str.data);
                        }
                        return *this;
                }
                virtual ~MyString() {
                        if (this->data) free(data);
                }
};

int main() {
        MyString a;
        a = MyString("Hello");
        MyString b(a);//MyStrig b = a;也是拷贝构造函数的
        b = a;
        std::vector<MyString> vec;
        vec.push_back(MyString("World"));
        return 0;
}

#输出
Move Assignment ! source: Hello
Copy Constructor ! source: Hello
Copy Assignment ! source: Hello
Move Constructor ! source: World

这里还做了个小实验,如果把移动拷贝构造函数和移动拷贝赋值函数去掉,会怎么样,下面是输出。

Copy Assignment ! source: Hello
Copy Constructor ! source: Hello
Copy Assignment ! source: Hello
Copy Constructor ! source: World

看来是当作左值处理了。那么问题来了,为什么左值引用也能用右值来初始化呢?原因很简单,因为我的代码中用了常量左值引用,常量左值引用可以用右值来初始化的。如果去掉函数参数中的const修饰,那么会报错。

4.std::move()

有了上面的基础,我们就可以引出std::move()了。这个函数做的事情其实很简单,就是把传入的符号强行转换成右值引用。它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue);

可见其函数流程是通过右值引用传递模板实现,利用引用折叠原理将右值经过T&&传递类型保持不变还是右值,而左值经过T&&变为普通的左值引用,以保证模板可以传递任意实参,且保持类型不变。然后通过static_cast<>进行强制类型转换返回T&&右值引用,而static_cast<T>之所以能使用类型转换,是通过remove_refrence<T>::type模板移除T&&,T&的引用,获取具体类型T。

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

到此,我们就可以分析一下下面代码的流程了。main函数中第3行,就是先把符号a转化为右值,所以push_back()会调用string类的移动拷贝构造函数,参照MyString类型的移动拷贝构造函数,这样就节省了char*的深复制过程。

int main(){
    vector<MyString> st;
    MyString a = "123";
    st.push_back(std::move(a));
    return 0;
}
 

5.结合unique_ptr理解

我们再通过智能指针中的unique_ptr来深入了解一下其机制。unique_ptr的特点是独占式的,即它不允许执行拷贝赋值函数。如果需要传递所指向的地址,要使用std::move()函数。那unique_ptr是怎么做到这2个特点的呢,只需要看unique_ptr的实现即可。阅读下面代码,可以发现拷贝构造函数、拷贝赋值函数都被设置为delete,unique_ptr自然不可通过“=”传递值。unique_ptr实现了移动拷贝构造函数和移动拷贝赋值函数,自然也就能通过pb = std::move(pa)这样的语法来传递值了。

template<typename T>
class MyUniquePtr
{
public:
   explicit MyUniquePtr(T* ptr = nullptr)
        :mPtr(ptr)
    {}

    ~MyUniquePtr()
    {
        if(mPtr)
            delete mPtr;
    }

    MyUniquePtr(MyUniquePtr &&p) noexcept;
    MyUniquePtr& operator=(MyUniquePtr &&p) noexcept;

    MyUniquePtr(const MyUniquePtr &p) = delete;
    MyUniquePtr& operator=(const MyUniquePtr &p) = delete;

    T* operator*() const noexcept {return mPtr;}
    T& operator->()const noexcept {return *mPtr;}
    explicit operator bool() const noexcept{return mPtr;}

    void reset(T* q = nullptr) noexcept
    {
        if(q != mPtr){
            if(mPtr)
                delete mPtr;
            mPtr = q;
        }
    }

    T* release() noexcept
    {
        T* res = mPtr;
        mPtr = nullptr;
        return res;
    }
    T* get() const noexcept {return mPtr;}
    void swap(MyUniquePtr &p) noexcept
    {
        using std::swap;
        swap(mPtr, p.mPtr);
    }
private:
    T* mPtr;
};

template<typename T>
MyUniquePtr<T>& MyUniquePtr<T>::operator=(MyUniquePtr &&p) noexcept
{
    swap(*this, p);
    return *this;
}

template<typename T>
MyUniquePtr<T> :: MyUniquePtr(MyUniquePtr &&p) noexcept : mPtr(p.mPtr)
{
    p.mPtr == NULL;
}

使用unique_ptr的示例,不能使用拷贝构造函数,可以使用移动拷贝构造函数,使用之前先把pa转为右值即可。

int main(){  
    unique_ptr<string> pa(new string("CHN"));
    unique_ptr<string> pb(new string("USA"));

//  pb=pa;//错误,不能使用拷贝赋值函数
//  pb = move(pa); 正确
//  pb = static_cast<unique_ptr<string>&&>((unique_ptr<string>&&)pa);正确
//  pb = (unique_ptr<string>&&)pa;正确
    unique_ptr<string>&& tt = (unique_ptr<string>&&)pa;
    cout<<__LINE__<<*tt<<endl;//CHN
    cout<<__LINE__<<*pa<<endl;//CHN
    return 0;
}

其实目前我比较存疑的是,声明一个右值引用,指向pa这句代码是怎么执行的,貌似也没调用什么类的成员函数,g++ -S汇编看了一下,是下面这个样子,也没看出来什么特别之处,可能这句代码只是声明一个变量?希望有明白的大佬赐教。

    .cfi_startproc                                 
    .cfi_personality 0x3,__gxx_personality_v0      
    .cfi_lsda 0x3,.LLSDA2299                       
    pushq   %rbp    #                              
    .cfi_def_cfa_offset 16                         
    .cfi_offset 6, -16                             
    movq    %rsp, %rbp  #,                         
    .cfi_def_cfa_register 6                        
    pushq   %r12    #                              
    pushq   %rbx    #                              
    subq    $64, %rsp   #,                         
    .cfi_offset 12, -24                            
    .cfi_offset 3, -32                             
    movq    %fs:40, %rax    #, tmp154              
    movq    %rax, -24(%rbp) # tmp154, D.49152      
    xorl    %eax, %eax  # tmp154                   
    leaq    -48(%rbp), %rax #, tmp112              
    movq    %rax, %rdi  # tmp112,                  
    call    _ZNSaIcEC1Ev    #                      
    leaq    -48(%rbp), %r12 #, D.49146             
    movl    $32, %edi   #,                         
.LEHB3:                                            
    call    _Znwm   #                              
.LEHE3:                                            
    movq    %rax, %rbx  # tmp113, D.49147          
    movq    %r12, %rdx  # D.49146,                 
    movl    $.LC1, %esi #,                         
    movq    %rbx, %rdi  # D.49147,                 

6.参考博客,以及更多引申内容

std::move()的实现,类型推导机制https://blog.csdn.net/p942005405/article/details/84644069

unique_ptr实现代码https://www.jianshu.com/p/77c2988be336

通用引用、完美转发https://www.jianshu.com/p/d19fc8447eaa

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

听说还有人不懂右值和std::move()? 的相关文章

  • Python的命令行参数解析

    文章作者 xff1a Tyan 博客 xff1a noahsnail com CSDN 简书 命令行参数解析在编程语言中基本都会碰到 xff0c Python中内置了一个用于命令项选项与参数解析的模块argparse 下面主要介绍两种解析P
  • Matlab 2016a/b中调用GPU速度巨慢的解决办法

    利用caffe的MATLAB接口跑深度学习时 xff0c 设置gpu模式 xff1a caffe set mode gpu xff0c 可以加速运算 xff0c 然而在MATLAB 2016a b中调用gpu时会出现了一个BUG xff0c
  • keras 2.3.0 做上采样 UpSampling2D的时候的维度出错问题解决办法

    简单的说 xff0c 你是不是遇到了这样的问题 xff0c 上一层的数据是 None xff0c 200 14 14 你希望上采样到28x28 H 61 UpSampling2D size 61 2 2 H 你以为能得到 None xff0
  • juju based openstack upgrade (by quqi99)

    作者 张华 发表于 2022 02 17 版权声明 可以任意转载 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 http blog csdn net quqi99 问题 客户想将juju管理的openstack从xenia
  • Try Fyde OS on VMWare and Surface (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 02 28 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 http blog csdn net quqi99 Insta
  • Installing third-party firmware on x3-55 letv (by quqi99)

    问题 趁贾老板明天回国之前 xff0c 得连夜将他的乐视x3 55电视刷成第三方精简版的固件 xff0e 官方固件安装的内置服务太多不仅占硬盘空间而且都开着也占用内存影响运行速度 xff0e 要安装的是 xff02 蓝同学 xff02 的固
  • Set up debian based maas ha env on xenial by hand (by quqi99)

    准备三个节点 本文将在xenial ubuntu 16 04 使用debian包手工创建maas ha环境 先快速准备三个节点 juju deploy ubuntu maas1 series xenial config hostname m
  • add a wifi AP for armbian box (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 03 26 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 http blog csdn net quqi99 无线网卡的
  • Kids are forbidden to watch TV after school (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 03 30 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 http blog csdn net quqi99 iptab
  • ubuntu 20.04升级到22.04中遇到的问题(by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 04 23 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 http blog csdn net quqi99 昨天通过
  • keil下载代码时出现:“Not a genuine ST Device! Abort connection“的错误

    最近在学习嵌入式 xff0c 难免要玩一些开发板 我选择了相对比较便宜的STM32F10C8T6 所以我就从网上购买了这快板子 刚开始买回来的时候 xff0c 我根本不知道往板子上烧录代码的时候还需要ST LINK 因为我在学F407的时候
  • Testing ovn manually based on LXD (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 05 27 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 准备两个LXD容器 lxc list 43 43 43 43
  • [WIP] Openstack Masakari (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 06 07 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 什么是masakari masakari是OpenStack
  • 远程解决win10上keyboard和chrome不work的两例问题(by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 06 10 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 远程解决了两例windows问题 xff0c 记录一下 xff
  • try anbox or waydroid (by quqi99)

    作者 张华 发表于 2022 06 28 版权声明 可以任意转载 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 无论是安装anbox还是waydroid都失败了 记录一下 里面首先是没有 dev binder的问题 那是因
  • set up ovn development env (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 07 08 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 编译ovs并启动ovs vswitchd https docs
  • Using lxd to do vlan test (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 08 15 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 问题 客户说sriov虚机里收不着arp reply 他们的s
  • ovn metadata (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 08 25 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 问题 客户描述虚机的metadata功能偶尔有问题 xff0c
  • 网络攻防实验 (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 08 29 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 测试环境 lxd容器 xff0c i3为中间攻击者所以在i3上
  • nova VirtualInterfaceCreateException (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 09 01 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 问题 虚机有时候会报下列错误 xff1a nova excep

随机推荐

  • ovn-central raft HA (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 10 12 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 What s raft RAFT https raft git
  • Linux(五):Ubuntu 16.04 更改系统语言为简体中文(Chinese simplified)

    Linux xff08 五 xff09 xff1a Ubuntu 16 04 更改系统语言为简体中文 xff08 Chinese simplified xff09 文章目录 1 问题2 设置中文2 1 设置 xff1b 2 2 点击 Ins
  • juju创建lxd容器时如何使用本地镜像(by quqi99)

    作者 xff1a 张华 发表于 xff1a 2023 03 01 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 问题 没有外网 xff0c 所以配置了一个local cust
  • my cloud test bed (by quqi99)

    作者 张华 发表于 2023 03 10 版权声明 可以任意转载 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 问题 有一台NUC minipc 配置是 CPU i7 13700H 16核20线程 内存 16G 32G 4
  • try chatgpt api (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2023 03 23 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 chatgpt web 试了网上的一个chatgpt web
  • ubuntu操音量调整命令amixer

    1 解除静音 sudo amixer set 39 Master 39 unmute sudo amixer set 39 Headphone 39 unmute sudo amixer set 39 Front 39 unmute 实际为
  • IAR软件应用中的错误提示

    1 Q xff1a Error e16 Segment XDATA Z size 0x19a1 align 0 is too long for segment definition At least 0xe4c more bytes nee
  • 软件工程—结构化分析设计

    进行完需求分析 xff0c 下一步该进行系统结构分析和设计了 现在主流的设计理念为结构化开发和面向对象开发 xff0c 本次主要讲解结构化开发 结构化开发分为结构化分析和设计两个阶段 结构化分析是面向数据流的分析方法 xff0c 基本思想是
  • 项目流标了怎么办?

    公开招标的项目如果出现流标现象 xff0c 是不能直接采取议标的方式 xff0c 或者直接采取竞争性谈判 单一来源采购 询价等非招标方式 xff0c 而应当组织二次招投标 xff0c 如果二次招标后仍然流标的 xff0c 可以核准后不再招标
  • xubuntu 14.04 LTS安装拼音输入法

    一 xff0c 安装fcitx sudo apt get install fcitx table wbpy 是不是很好记的样子 xff0c wb五笔py拼音 二 xff0c 配置fcitx desktop gnome language se
  • 整理库函数依赖关系

    问题 xff1a 现有一函数库 xff0c 这里是lapack3 5 lapack提供的每一个函数API都单独是一个 c 请给出这些API的相互调用关系 间接调用也要统计 xff0c 循环调用 xff08 如果可能的话 xff09 不计 进
  • 手把手教你写出正确的二分搜索!

    写出正确的二分搜索知易行难 xff0c 原理好像都懂 xff0c 但是实际上手就出各种错误 xff0c 例如如何确定循环终止条件 区间搜小判断条件等 这里就手把手教你写出正确的二分检索 xff01 二分法共有下面7种变式 是否存在数字t 返
  • SphereFace: Deep Hypersphere Embedding for Face Recognition

    Weiyang Liu 1 Yandong Wen 2 Zhiding Yu 2 Ming Li 3 Bhiksha Raj 2 Le Song 1 1 Georgia Institute of Technology 2 Carnegie
  • 一文解答你关于“轨道问题”的所有疑问!(有环链表问题)

    问题描述 xff1a 给定一个链表 xff0c 返回链表开始入环的第一个节点 如果链表无环 xff0c 则返回 NULL 我看过很多博客 xff0c 对于最优解法的解释无非两个字 xff0c 神奇 xff0c 并没有说明如何构思出这样的思路
  • 从bt到dp的困惑

    每一个dp的背后都必然有一个bt bt的基础上加上记忆化搜索 xff0c 然后对问题的拆分由从上到下变成从下到上 xff0c 即可转化为dp 但绝不是所有的bt就天然的可以转化为dp 例如下面这个例子 leetCode 115题 xff0c
  • 教练,我想学二叉树遍历!

    二叉树具有前序 中序 后序三种遍历方式 递归的相信大家都会写 xff0c 但是迭代的该怎么写呢 xff1f 怎样的迭代写法才能具有统一性便于理解呢 xff1f 请看下面具体的做法 xff0c 每种遍历方式各有2种策略 xff0c 基于栈的和
  • TVM优化原理学习

    没有看论文 xff0c 看了b站陈天奇的视频 xff0c 还有一些博客的分析 xff0c 学习了优化原理 后续需要深入理解再看论文 TVM对于神经网络的优化主要有两部分 xff0c 计算图优化和算子优化 xff0c 下面分开说明 计算图优化
  • 一个GDB调试的workflow

    我们要调试的程序是 include lt stdio h gt int main int p 61 NULL printf 34 p n 34 p p 61 3 printf 34 d n 34 p return 0 可以看到第6行会访问非
  • 记录一个很奇怪的bug,待解决

    一个很简单的矩阵求幂模板类的程序 xff0c 但是在vector lt vector lt T gt gt temp n vector lt T gt n 这一句不能执行 xff0c 会卡死 下面是完整的代码和输出 方阵的幂运算 xff0c
  • 听说还有人不懂右值和std::move()?

    1 左值和右值 左值是可以出现在等号左边的符号 xff0c 当然它也可以出现在等号右边 xff0c 例如int a等等 右值是能且只能出现在等号右边的符号 xff0c 例如5 xff0c abc 等等 右值细分为将亡值和纯右值 xff0c