十、C++11左值右值、左值引用、右值引用、万能引用、完美转发

2023-11-08

10 C++11左值右值、左值引用、右值引用

10.1 左值&右值

左值:可以在=左边使用的值

右值:只能在=右边使用的值(字面量、中间结果、临时对象/匿名对象),无法直接取地址,不能使用左值引用。

10.2 左值引用&右值引用

实例:左值引用、右值引用

#include <iostream>
#include <string>
using namespace std;
void Print(int n){
    cout << n << endl;
}
void Print2(int& n){
    cout << n << endl;
}
void Print3(const int& n){
    cout << n << endl;
}
void Print4(int&& n){ //右值引用
    cout << n << endl;
}
int main(){
    //右值:只能在=右边使用的值(字面量\中间结果\临时对象或匿名对象),无法取地址,不能左值引用
    //左值:可以在=左边使用的值
    int n = 10;
    int m = n;
    //10 = n; //字面量不能左值引用
    //n+2 = m; //中间结果不能左值引用
    const char* s = "123abc";
    //"abcd" = s;
    string str = s;
    //string(s) = str; 临时对象不能左值引用
    
    Print(m);
    Print(10);

    Print2(m); //int& n = m;
    //Print2(10); //int& n = 10; 常量不能被引用
    //int& f = 10; //右值不能初始化左值引用

    Print3(m); //const int& n = m;
    Print3(10); //const int& n = 10; 补丁:const左值引用可以初始化左值和右值

    //Print4(m); int&& n = m; 左值不能初始化右值引用
    Print4(10);
}
[root@localhost 9]# ./a.out 
10
10
10
10
10
10

结论:
1、左值引用只能用左值初始化
2、右值引用只能用右值初始化
3、const左值引用可以初始化左值和右值

10.3 移动构造函数&移动赋值运算符重载

10.3.1 不可复制对象的移动

实例:

#include <iostream>
using namespace std;
class Uncopy{
public:
    Uncopy(){cout << "constructor" << endl;}
    ~Uncopy(){cout << "destructor" << endl;}
    Uncopy(const Uncopy&) = delete; //禁用赋值
    Uncopy& operator=(const Uncopy&) = delete; //禁用拷贝构造
    Uncopy(Uncopy&&){cout << "rvalue copy constructor" << endl;}
    Uncopy& operator=(Uncopy&&){cout << "rvalue assign" << endl; return *this;}
};
void Param(Uncopy w){}

Uncopy Return(){
    Uncopy u;
    return u;
}
// 移动的条件:只有右值可以移动
// 左值变右值:move()
int main(){
    Uncopy u;
    //Uncopy v = u; //拷贝构造
    Uncopy w = Uncopy();
    Uncopy v;
    v = move(u); //赋值运算符重载
    v = Uncopy(); //移动运算符重载

    //Param(Uncopy()); //Uncopy w = Uncopy();
    Param(move(u)); //Uncopy w = u; 
    Return();
}
constructor
constructor
rvalue copy constructor
destructor
constructor
rvalue assign
constructor
rvalue assign
destructor
rvalue copy constructor
destructor
constructor
rvalue copy constructor
destructor
destructor
destructor
destructor
destructor

不可复制对象,可以使用移动语法做拷贝构造和赋值。

  • 移动的条件:
    1、只有右值才能移动,如果左值要移动需要转换成右值(move())。
    2、对象的类要实现移动构造函数和移动赋值运算符重载。

10.3.2 可复制对象的移动

实例:

#include <vector>
using namespace std;
//可复制对象使用移动
class Simple{
public:
    Simple(){cout << "constructor" << endl;}
    Simple(const Simple&){cout << "copy constructor" << endl;}
    Simple& operator=(const Simple&){cout << "assign constructor" << endl; return *this;}
    Simple (Simple&&){cout << "move constructor" << endl;}
    Simple& operator=(Simple&&){cout << "move override" << endl; return *this;}
    ~Simple(){cout << "destructor" << endl;}
    //加const只能放左值,不加const既能放左值,也能放右值
};
int main(){
    string s = "abc";
    //string t = s;
    string t = move(s);
    cout << (void*)s.c_str() << ":" << s << endl; //s执行move之后内容为空
    cout << (void*)t.c_str() << ":" << t << endl;

    vector<int> vec = {1,2,3};
    vector<int> vec2;
    vec2 = move(vec);
    cout << vec.size() << " " << vec2.size() << endl; //vec被move之后内容为空,大小为0

    //移动条件下,如果类内未定义移动函数,使用拷贝函数,定义了则使用移动函数
    Simple s1;
    Simple s2 = move(s1); //移动构造
    s2 = move(s1); //移动重载
    
    //STL中的移动
    vector<Simple> vs;
    //vs.push_back(s1); //拷贝构造
    vs.emplace_back(move(s1)); //移动构造
}

0x7fff2bf29880:
0x7fff2bf29860:abc
0 3
constructor
move constructor
move override
move constructor
destructor
destructor
destructor
  • 移动的条件:
    1、只有右值才能移动,如果左值要移动需要转换成右值(move())。
    2、对象的类实现了移动构造函数和移动赋值运算符重载 。否则,执行拷贝操作。

10.3.3 STL中的移动

实例:

#include <iostream>
#include <vector>
 
using namespace std;
// 可复制对象使用移动
class Simple{
public:
    Simple(){cout<< "constructor" << endl;}
    Simple(const Simple&){cout<< "copy constructor" << endl;}
    Simple& operator=(const Simple&){cout<< "assign override" << endl; return *this;}
    Simple(Simple&&){cout<< "move constructor" << endl;}
    Simple& operator=(Simple&&){cout<< "move override" << endl; return *this;}
};
int main(){
    string s = "abcd";
    // string t = s;
    string t = move(s);
    cout << (void*)s.c_str() << " " << s << endl;
    cout << (void*)t.c_str() << " " << t << endl;
 
    vector<int> vec1 = {1,2,3,4,5};
    vector<int> vec2;
    vec2 = move(vec1);
    cout << vec1.size() << "," << vec2.size() << endl;
 
    vector<Simple> vs;
    Simple simple;
    //vs.push_back(simple); // 拷贝构造
    //vs.push_back(simple); // 拷贝构造
    //vs.push_back(simple); // 拷贝构造
    //vs.push_back(simple); // 拷贝构造
    vs.emplace_back(move(simple)); // 移动构造
    vs.emplace_back(move(simple)); // 移动构造
    vs.emplace_back(move(simple)); // 移动构造
}
0x7ffc13aebf70 
0x7ffc13aebf50 abcd
0,5
constructor
move constructor
move constructor
copy constructor
move constructor
copy constructor
copy constructor

C++11 string和容器都实现移动构造函数和移动赋值运算符重载。
C++11 STL容器提供了移动操作的成员函数emplace_*()

结果分析:
这里vsmove第一个simple对象时候申请1个空间并放入vs[0]move第二个simple时候,先申请2个空间,move第二个simple 放入vs[1],再把第一个simple拷贝到vs[0]的位置。同理,move第三个simple时候,申请4个空间,move第三个simplevs[2],然后拷贝前两个simple分别放入vs[0]vs[1]中。

注:vector每次申请1、2、4、8…个空间

10.4 万能引用&完美转发

实例:

#include <iostream>
#include <vector>
using namespace std;
//可复制对象使用移动
class Simple{
public:
    Simple(){cout << "constructor" << endl;}
    Simple(const Simple&){cout << "copy constructor" << endl;}
    Simple& operator=(const Simple&){cout << "assign override" << endl; return *this;}
    Simple (Simple&&){cout << "move constructor" << endl;}
    Simple& operator=(Simple&&){cout << "move override" << endl; return *this;}
    
    //左值调用左值Test
    void Test()&{ //void Test(Simple*& this)
    	cout << "lvalue Test" << endl;
    }
    //右值调用右值Test
    void Test()&&{ //void Test(Simple*&& this)
    	cout << "rvalue Test" << endl;
    }
};
void Func(Simple& s){cout << "lvalue" << endl;}
void Func(Simple&& s){cout << "rvalue" << endl;}

template<typename T>
//模板参数&&:万能引用/通用引用,根据传入参数左右值类型不同,调用不同参数类型的函数
void TemplateFunc(T&& param){
    //Func(param);
    //完美转发
    Func(forward<T>(param));
    forward<T>(param).Test();
}
int main(){
    Simple s;
    Func(s); //左值
    Func(move(s)); //右值
    
    s.Test(); //调用左值Test
    Simple().Test(); //调用右值Test

    TemplateFunc(s); //传入左值 T&& => T& void TemplateFunc (Simple& param)
    TemplateFunc(Simple()); //传入右值 T&& ==> T&& void TemplateFunc(Simple&& param)
}
[root@localhost 9]# ./a.out 
constructor
lvalue
rvalue
lvalue Test
constructor
rvalue Test
lvalue
lvalue Test
constructor
rvalue
rvalue Test

1、函数参数中的&&表示右值引用,函数模板参数中的&&表示万能引用。通用引用/万能引用,根据参数左右值类型的不同,生成不同参数类型的函数
2、完美转发 forward<T>()根据万能引用参数的不同(左值引用还是右值引用),恢复参数的状态(左值引用保持左值,右值引用转成右值),实现函数调用的转发(传入左值,调用左值引用的函数;传入右值,调用右值引用的函数)。

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

十、C++11左值右值、左值引用、右值引用、万能引用、完美转发 的相关文章

随机推荐

  • homebrew安装mysql及修改配置

    安装mysql数据库5 7 5前后版本有所差异 以前的版本会在安装的时候要去输入密码 5 7 6 版本会在安装的时候随机生成一个密码 用图形界面化安装的时候会显示密码 而用 brew install mysql命令安装的话 就不会显示密码了
  • Volocopter将在新加坡推出空中的士服务;铂金资本72亿美元收购海航旗下英迈

    今日看点 铂金资本 Platinum Equity 将以72亿美元收购海航旗下的英迈 Volocopter将在新加坡推出空中的士 希尔顿与锦江集团续签合作协议 将在华开逾600家希尔顿欢朋酒店 爱立信携手意大利电信及高通公司基于5G毫米波创
  • c# ThoughtWorks 生成二维码

  • 004 C++基础篇

    前言 大家好 本文将会为您带来内联函数 auto关键字 基于范围的for循环 指针空值nullptr相关知识 一 内联函数 什么是内联函数 以inline修饰的函数叫做内联函数 编译时C 编译器会在调用内联函数的地方展开 没有函数调用建立栈
  • 关于火绒邮件监控引起的扫描任意IP会有25和110端口反馈

    之前测试过公司的外网IP 因为之前一直很注意对外映射的端口 都限制了可以访问的IP地址和端口 所以之前扫描的时候是一个端口都扫描不出来的 最近闲的无事 想着再扫描试试 结果发现居然开放了25和110端口 我当时第一反应 我靠 不会有人已经黑
  • cookie登录_注册登录 与 Cookie

    开始之前 我们先简单了解一下 注册 登录 的流程 无代码 进入注册页面 用户填入用户名 密码 手机号等 注册信息 前端验证用户填入的信息是否符合要求 如果不符合要求 就提示用户输入错误 重新输入 如果符合要求 就将 注册信息 作为 post
  • ARP广播原理和数据包格式

    1 ARP广播原理 1 A 135 24 25 23 和同一网段内的主机B 135 24 25 32 通信 查找arp 表没有32 IP的MAC地址 判断A 和B 是在同一局域网内 A向局域网发送ARP广播 B收到广播后 会把自己的MAC地
  • Redis集群部署文档(centos7系统)

    Redis集群部署文档 centos7系统 Redis是一个高性能的键值存储系统 常用于缓存 消息队列等场景 在大规模应用中 为了提高性能和稳定性 可以采用Redis集群搭建方式 Redis集群采用分布式架构 将数据分散存储在多个节点上 提
  • 漏洞复现-永恒之蓝(MS17-010)

    目录 一 漏洞介绍 1 永恒之蓝是什么 2 漏洞原理 3 影响版本 二 实验环境 三 漏洞复现 四 利用exploit模块进行渗透 一 漏洞介绍 1 永恒之蓝是什么 永恒之蓝是指2017年4月14日晚 黑客团体Shadow Brokers
  • SVPWM的一些参数

    SVPWM的一些参数 1 直流母线电压利用率 一般我们所说的电压利用率是指逆变器输出线电压的基波幅值与直流母线电压之比值 可以看出 电压利用率并不是一个恒定值 因为逆变器输出线电压的基波幅值是变化的 因此一般只讨论最高电压利用率 SVPWM
  • 用C语言单片机编程为什么总是用无符号的数据类型来定义?

    注意 答案和题目均转自百度知道 char型可以表示数的范围是 128到127 所占位数是8位int型可以表示数的范围是 32768到32767 所占位数是16位假如用有符号的数据类型表示 如果你要表示的数是128 那你就得定义int型 用了
  • Openharmony环境搭建

    鸿蒙系统开发环境搭建 前言 工具下载 开发环境 HUAWEI DevEco Device Tool 开发环境的准备 Windows开发环境准备 获取软件 vscode 安装 安装Python 安装Node js 安装hpm 安装DevEco
  • android中跨进程通讯的4种方式

    android中跨进程通讯的4种方式 转自 http www androidsdn com article show 137 由于android系统中应用程序之间不能共享内存 因此 在不同应用程序之间交互数据 跨进程通讯 就稍微麻烦一些 在
  • 神经网络:LSTM基础学习

    1 LSTM简介 在时间序列数据学习中 传统的循环神经网络 RNN 存在较多的学习瓶颈和技术缺陷 而长短时记忆 LSTM 神经网络克服了循环神经网络的缺陷 使其在长时间序列数据学习训练中能克服梯度爆炸和梯度消失的瓶颈 展现出超强的长系列数据
  • 比例数据可视化——(pyecharts)

    本文数据来自于大数据可视化技术这本书 用于复习 1 饼图 1 1 基础饼图 import pandas as pd from pyecharts charts import Pie from pyecharts import options
  • 推荐前 6 名 JavaScript 和 HTML5 游戏引擎

    推荐 使用 NSDT场景编辑器 助你快速搭建3D应用场景 事实是 自从引入JavaScript WebGL API以来 现代浏览器具有直观的功能 使它们能够渲染更复杂和复杂的2D和3D图形 而无需依赖第三方插件 你可以用纯粹的JavaScr
  • springboot+mybatisplus 配置多数据源打印sql语句失效的问题

    目录 1 如果没有配置多数据源 下打印sql 只在yml里面配置就会报如下的信息 2 这里说一下解决方法两种方式 都是在多数据源配置文件下 加入代码解决 1 如果没有配置多数据源 下打印sql 只在yml里面配置就会报如下的信息 Creat
  • windows 环境下,浏览器如何拉起应用程序?

    由于浏览器的限制 我们无法在web程序中直接拉起应用程序 今天告诉大家一个方式 可以在一定程度上处理这个问题 首先用java程序生成一个注册表文件 package com fh controller system regedit impor
  • 初识IDA工具

    工具 IDA工具 链接 https pan baidu com s 1Zgzpws6l2M5j1wkCZHrffw 提取码 ruyu 里面有安装密码 PassWord qY2jts9hEJGy 里面分析32位和64位启动快捷方式 打开IDA
  • 十、C++11左值右值、左值引用、右值引用、万能引用、完美转发

    10 C 11左值右值 左值引用 右值引用 10 1 左值 右值 左值 可以在 左边使用的值 右值 只能在 右边使用的值 字面量 中间结果 临时对象 匿名对象 无法直接取地址 不能使用左值引用 10 2 左值引用 右值引用 实例 左值引用