掌握std::move和std::forward

【移动语义和精准转发系列二】std::move和std::forward 在我们开始讲解std::move之前,先来介绍一个概念:引用折叠。这个概念仅用于 typedef 和 模板类型参数 中。 在模板世界中,T&& 称为Universal Reference,即通用引用。这与普通函数中形参的&&是不同的,希望不要弄混了。 对于模板函数template void func(T&& t),对于左值实参,T&& t 推导出的T为string&;对于右值实参,T&& t 阅读详情

在讲解std::move和std::forward之前,我们必须先了解C++中左值、右值的相关概念。

1、左值、右值

(1)左值:一般指的是在内存中有对应的存储单元的值,最常见的就是程序中创建的变量。
(2)右值:一般指的是没有对应存储单元的值(寄存器中的立即数,中间结果等),例如一个常量,或者表达式计算的临时变量。

判断某个表达式是左值还是右值的方法:

(1)可位于 = 左侧的表达式就是左值;反之,只能位于 = 右侧的表达式就是右值。

// 其中x是一个左值,字面值5是一个右值
int x = 5;

// 错误,5 不能为左值
5 = x;

说明:C++中的左值也可以当作右值使用。

// y 是一个左值
int y = 10;

// x、y 都是左值,但可以将 y 可以当做右值使用
x = y;

(2)有名称的、可以获取到存储地址的表达式即为左值;反之则是右值。

上述示例中变量 x、y 是变量名且通过 &x 和 &y 可以获得他们的存储地址,因此 x 和 y 都是左值;反之,字面量 5、10,它们既没有名称,也无法获取其存储地址,因此 5、10 都是右值。

2、左值引用、右值引用

(1)左值引用:C++中采用&对变量进行引用,我们平时熟悉的引用就是左值引用。

    int num = 10;
    int& b = num; //正确
    int& c = 10; //错误

    const int& c = 10; //正确

(2)右值引用:

因为右值本身也没有对应的存储单元,所以没法进行引用。右值引用实际上只是一个逻辑上的概念,最大的作用就是让一个左值达到类似右值的效果(下面程序举例),让变量之间的转移更符合“语义上的转移”,以减少转移之间多次拷贝的开销。右值引用符号是&&。

注意:
(1)右值引用不支持引用左值;
(2)非常量右值引用可以引用的值的类型只有非常量右值;
(3)常量右值可以引用常量右值、非常量右值。

int num1 = 10;
const int num2 = 100;

int&& a = num1;	//编译失败,非常量右值引用不支持引用非常量左值
int&& b = num2;	//编译失败,非常量右值引用不支持引用常量左值
int&& c = 10;	//编译成功,非常量右值引用支持引用非常量右值

const int&& d = num1;	//编译失败,常量右值引用不支持引用非常量左值
const int&& e = num2;	//编译失败,常量右值引用不支持引用常量左值
const int&& f = 100;	//编译成功,常量右值引用支持引用右值

3、move()函数

move()函数将左值强制转换成右值。但move()并没有移动能力!

int num = 10;
int&& a = std::move(num);  //编译成功
std::cout << a << std::endl;   //输出结果为10;
std::cout << num << std::endl; //输出结果为10;move只是把左值强制转换成右值,并没有移动能力。

a = 20;
std::cout << num << std::endl; //输出20

为了加深对move()的印象,我们再来看一个例子:

std::string str1 = "I love C++";
std::string str2 = std::move(str1);
std::cout << "str2:" << str2 << std::endl;
std::cout << "str1:" << str1 << std::endl;

执行结果:

我们看到执行move()函数后,str1里的内容没了,难道被移走了?我们前面说过move()没有移动能力,造成str1的内容为空的原因是string的“移动构造函数”。 我们再来看一个例子:

std::string str3 = "I love C";
std::string&& def = std::move(str3);
std::cout << "str3:" << str3 << std::endl;
std::cout << "def:" << def << std::endl;

执行结果:

4、forward()函数

std::forward的作用是完美转发,如果传递的是左值转发的就是左值引用,传递的是右值转发的就是右值引用。

std::move可以减少不必要的拷贝开销,可以提高程序的效率。但是std::forward的作用是转发,左值引用转发成左值引用,右值引用还是右值引用,它的意义到底是什么?

原来是在程序的执行过程中,对于引用的传递实际上会有额外的隐式的转化,一个右值引用参数经过函数的调用转发可能会转化成左值引用,但这就不是我们希望看到的结果。

举例:

#include <iostream>

template<typename T>
void print(T& t) {
    std::cout << "左值" << std::endl;
}

template<typename T>
void print(T&& t) {
    std::cout << "右值" << std::endl;
}

template<typename T>
void testForward(T&& v) {
    print(v);
    print(std::forward<T>(v));
    print(std::move(v));
}

int main(int argc, char* argv[])
{
    testForward(1); // 传入右值

    std::cout << "======================" << std::endl;

    int x = 1;
    testForward(x); // 传入左值
}

执行结果:

 

参考:

(1)[C++特性]对std::move和std::forward的理解

(2)【C++】左值和右值、左值引用(&)和右值引用(&&)

(3)抄袭李超老师的std::forward()

Effective Modern C++ 条款23:理解std::move和std::forward 前者是典型地为了移动操作,而后者只是传递(亦为转发)一个对象到另外一个函数,保留它原有的左值属性或右值属性。的吸引力在于它的便利性:减少了出错的可能性,增加了代码的清晰程度。是移动操作的候选者。接受一个对象的引用(准确的说,一个通用引用(universal reference),见。的,因为我们可以到处直接写转换代码,但是我希望我们能同意:这将相当的,嗯,让人恶心。返回的真的是右值引用,这很重要,因为函数返回的右值引用是右值。,你能确保的唯一一件事就是将它应用到一个对象上,你能够得到一个右值。 阅读详情

相关推荐

从踩坑到填平:我在RHEL7上给Tesla A100装驱动的完整记录(含Fabric Manager配置)

本文详细记录了在RHEL7系统上为Tesla A100安装Nvidia驱动的完整过程,包括Fabric Manager配置的关键步骤。通过分析A100架构的特殊性,提供了从驱动安装到服务配置的详细指南,并分享了常见问题的排查方法和性能调优技巧,帮助用户高效部署高性能GPU计算环境。

weixin_29291863的博客 283

理解std::move和std::forward

首先通过了解它们(指std::move和std::forward)不做什么来认识std::move和std::forward是非常 有用的。std::move不move任何东西。std::forward也不转发任何东西。在运行时,他们什么 都不做。不产生可执行代码。 std::move和std::forward只是执行转换的函数(确切的说应该是函数模板)。std::move无条件的 将它的参数转换成一个右值,而std::forward当特定的条件满足时,才会执行它的转换。这就 是它们本来的样子.这样的解

猫哥 的沉淀、积累、总结。天天学习,好好向上...c/c++,嵌入式 linux,Android,HarmonyOS,AIOT) 3863

VASP+机器学习快速收敛AIMD

本文介绍了如何利用VASP结合机器学习技术快速收敛AIMD(从头算分子动力学)模拟。首先,通过训练机器学习力场,生成关键文件如ML_ABN(包含结构数据集)、ML_FFN(存储回归结果)和ML_LOGFILE(记录训练日志)。接着,配置INCAR文件以设置计算参数,包括并行化、分子动力学和机器学习相关参数。KPOINTS文件用于定义K点网格。计算完成后,备份CONTCAR文件作为后续模拟的初始结构。最后,利用训练好的力场文件进行纯机器学习力场分子动力学模拟,并提交任务执行。

qq_44812865的博客 1406

C++的std::move与std::forward

一、背景介绍 什么是左值和右值? 1、左值引用 C/C++语言中可以放在赋值符号左边的变量,即具有对应的可以由用户访问的存储单元,并且能够由用户去改变其值的量。或者说左值是代表一个内存地址值,并且通过这个内存地址,就可以对内存进行读并且写(主要是能写)操作。相对应的还有右值:当一个符号或者常量放在操作符右边的时候,计算机就读取他们的“右值”,也就是其代表的真实值。 简单来说就是,左值相当于地址值,右值相当于数据值。右值指的是引用了一个存储在某个内存地址里的数据。 左值右...

sinat_31608641的博客 2747

软件工程导论实践:从需求到架构的机票预订系统全流程解析

本文深入解析机票预订系统从需求分析到架构设计的全流程实践,涵盖业务流程、功能建模、模块化设计、性能优化等关键环节。通过实际项目经验,分享软件工程导论在复杂系统开发中的应用技巧,包括数据流图绘制、E-R图设计、微服务架构及高并发处理等实战策略,助力开发者构建高效可靠的机票预订系统。

weixin_29202213的博客 414

【数字IC设计】Centos 7桌面版 安装Synopsys EDA Tools

简称DC,是Synopsys公司用于做电路综合的核心工具,可以将HDL描述的电路转换为基于工艺库的门级网表。

qiumingT的博客 9803

C++的std::move与std::forward原理大白话总结

阅读大型的C++开源项目代码,基本逃不过std::move和std::forward,例如webRTC。 所以搞懂其原理,很有必要。 网络上已有不少文章介绍(见@参考),但是比较分散,所以我把自认为的关键点,加上一些自己的想法,提取总结一下。 1. std::move 别看它的名字叫move,其实std::move并不能移动任何东西,它唯一的功能是将一个左值/右值强制转化为右值引用,继而可以通过右值引用使用该值,所以称为移动语义。 1.1 函数原型 函数定义原型: /** * @brief Conve

newchenxf的专栏 1万+

Python爬虫实战:构建药监局药品批准信息本地查询系统

在医药信息化时代,药品批准信息的快速查询对于医药从业者、研究人员和普通消费者都具有重要意义。国家药品监督管理局(NMPA)提供了官方的药品查询系统,但有时我们需要建立本地数据库以便批量分析或离线查询。本文将详细介绍如何使用Python爬虫技术,结合最新的异步爬虫框架和技术栈,构建一个完整的药监局药品信息本地查询系统。本系统将实现以下功能:自动化爬取药监局官网的药品批准信息数据清洗和结构化存储建立本地查询接口数据定期更新机制pythonimport os"""药品类型枚举"""

2201_76125261的博客 728

std::move与std::forward

std::move与std::forward

ELCstar的专栏 1087

总结目前开源的视频生成/视频编辑大模型

随着Sora的爆火,视频生成和视频编辑受到了越来越多的关注。这篇博客总结一下目前开源的视频生成和视频编辑的大模型,并分析他们各自的优缺点,以及在自己进行科研任务或者工作中应该如何选用对应的开源模型,希望能给从事这一领域研究和使用的小伙伴们帮助。

沉迷单车的追风少年 3544

PID控制算法实践应用(一):PID算法的离散化

由于计算机控制是一种采样控制,它只能根据采样时刻的偏差值计算控制量。因此,连续PID控制算法不能直接使用,需要采用离散化方法。

m0_72676510的博客 2602

C++11中std::move和std::forward到底干了啥

C++11中的右值引用的出现,在特定情况下减少了对象的拷贝,提升了C++程序的效率,伴随而来的 `std::move` 和 `std::forward` 也大量出现在程序代码中,但是这两个函数究竟干了啥呢?其实他们的本质都是转换函数,也就是完成左值和右值之间的转换,需要注意的是左值可以转换成右值,但是右值无法转换成左值...

AlbertS Home of Technology 6674

微信小程序一键跳转公众号(使用 official-account 组件)

在当今移动互联网时代,用户体验已成为应用成功的关键因素之一。微信小程序作为一种便捷的应用形式,广泛应用于各个行业,为用户提供了丰富的服务和信息。然而,如何进一步提升用户体验,使其在小程序中能够更方便地获取相关信息,成为开发者需要关注的重要课题。实现一键跳转到公众号的功能,正是一个有效的解决方案。通过这一功能,用户可以轻松访问公众号,获取更多内容和服务,从而增强与品牌的互动和粘性。本文将详细介绍如何在微信小程序中实现这一功能,并提供相关的操作步骤和注意事项。

不迁怒,不贰过。小知识,大智慧。 3127

浅谈std::move和std::forward原理

本文主要整理了C++11中std::move和std::forward的原理, 这对理解C++的移动拷贝有很重的意义。

爱拼才会赢 3975

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

std::move和std::forward

std::move是c++11引入的,用于将一个资源"移动"到另一个对象,而不是复制。但是通过std::move的源码可以发现,其底层实际并没有"转移"的动作,那是如何实现呢?通过源码可以发现,std::move的底层实际只是进行了类型强制转换,将一个变量强制转换成了一个右值。1.remove_reference_t:用于类型转换,用于移除_Ty的引用,如果_Ty是T&或者T&&,都会变成T类型2._Arg是函数参数,并且是一个右值引用,因此参数可以是左值也可以是右值。

weixin_47326789的博客 586

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

C++理解std::move和转发(std::forward)

/在返回类型和类型转换中也要用到typename这段代码很短,但其中有些微妙之处。首先,move的函数参数T&&是一个指向模板类型参数的右值引用。通过引用折叠,此参数可以与任何类型的实参匹配。特别是,我们既可以传递给move一个左值,也可以传递给它一个右值,"),s2;"));// 正确:从一个右值移动数据// 正确:但在赋值之后,s1的值是不确定的//没有打印任何东西为了通过翻转函数传递一个引用,我们需要重写函数,使其参数能保持给定实参的“左造性”。

2301_80224556的博客 1622

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

条款23:理解std::move和std::forward

Effective Modern C++之条款23

weixin_43739503的博客 930

C++:std::move 和 std::forward

先看一个简单的示例,注意他们的输出结果。

彭世瑜的博客 1289

C++11 std::move和std::forward

https://www.jianshu.com/p/b90d1091a4ff 下文先从C++11引入的几个规则,如引用折叠、右值引用的特殊类型推断规则、static_cast的扩展功能说起,然后通过例子解析std::move和std::forward的推导解析过程,说明std::move和std::forward本质就是一个转换函数,std::move执行到右值的无条件转换,std::forwa...

$firecat利白的代码足迹$ 1057

std::move和std::forward 深度剖析

1 引入的规则 规则1(引用折叠规则):如果间接的创建一个引用的引用,则这些引用就会“折叠”。 一般情况下,引用折叠成一个普通的左值引用类型。 X& &、X& &&、X&& &都折叠成X& 一种特殊情况下,引用会折叠成右值引用,即右值引用的右值引用, T&& &&。 X&& &&折叠为X&& 规则2(右值引用的特殊类型推断规则):当将

qq_27727147的博客 769

std::move和std::forward的区别

std::move:“我要移动这个对象的资源”—— 强制转为右值,用于明确触发移动语义。:“按原样转发这个参数”—— 保持原始值类别,用于模板中实现完美转发。move是“无条件右值化”,forward是“有条件保持原值类别”。

u010632343的博客 533

c++中std::move和std::forward

std::move无条件转换到右值。就其本身而言,它没有move任何东西。 std::forward只有在它的参数绑定到一个右值上的时候,它才转换它的参数到一个右值。 std::move和std::forward在运行期都没有做任何事情。它们没有产生需要执行的代码,1个byte都没有。 std::move和std::forward只不过就是执行cast的两个函数(实际上是函数模板)。 std...

qq_35331967的博客 435

std::move 和 std::forward

特性std::move目的强制转为右值,触发移动语义保持参数原始值类别,完美转发转换条件无条件依赖模板参数T典型应用移动构造函数、避免拷贝泛型代码中的参数转发参数类型接受任意类型通常与万能引用T&&配合使用。

Black_Silencer的博客 548

C++ std::move 和 std::forward 实现

std::forward 仅仅对绑定到右值的引用实施向右值类型的强制类型转换,即仅当传入的实参被绑定到右值时候,std::forward才强制转化为右值。std::move 和 std::forward 都是仅仅执行强制类型转换的函数(底层为函数模板),2. std::forward的实现, 参考visual studio实现。3. remove_reference_t 实现如下。std::move 无条件的将实参强制转化为右值,1. std::move 的实现。

MyBigOrange的博客 450
上一篇: c++中的时间处理(3)与sleep相关的时间函数
下一篇: C++内存池(1)理论基础及简单实现
老蔡编程
博客等级 码龄23年 48粉丝 75原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值