爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      【C++11】move和forward 、左值右值(性能优化之道)

      首页 知识中心 软件开发 文章详情页

      【C++11】move和forward 、左值右值(性能优化之道)

      2025-02-10 08:56:02 阅读次数:17

      变量,右值

      简单记忆

      move避免复制,forward避免重载(遇到右值move,遇到左值复制)

      std::move

      std::move函数可以以非常简单的方式将左值引用转换为右值引用。(左值、左值引用、右值、右值引用 参见:

      左值和右值:【C/C++】理解C和C++中的左值和右值_bandaoyu的博客-CSDN博客

      不正确但便于理解的解释:(左值引用转换为右值引用:b = 2;a = b;  b是左值,有地址。 如果b是临时变量,用完就放弃,那就用move将他变成右值,同时它的地址赋予a,就避免了拷贝操作。a = move(b))

      通过std::move,可以避免不必要的拷贝操作。

      std::move是为性能而生。

      std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。

      【C++11】move和forward 、左值右值(性能优化之道)

       

      如string类在赋值或者拷贝构造函数中会声明char数组来存放数据,然后原string中的 char 数组被析构函数释放,如果a是一个临时变量,则上面的拷贝,析构就是多余的,完全可以把临时变量a中的数据直接 “转移” 到新的变量下面即可。

      #include <iostream>
      #include <utility>
      #include <vector>
      #include <string>
      int main()
      {
          std::string str = "Hello";
          std::vector<std::string> v;
          //调用常规的拷贝构造函数,新建字符数组,拷贝数据
          v.push_back(str);
          std::cout << "After copy, str is \"" << str << "\"\n";
          //调用移动构造函数,掏空str,掏空后,最好不要使用str
          v.push_back(std::move(str));
          std::cout << "After move, str is \"" << str << "\"\n";
          std::cout << "The contents of the vector are \"" << v[0]
                                               << "\", \"" << v[1] << "\"\n";
      }

      move和forward虽然是8年前C++提出的新东西, 但要搞懂还是得费一些精力.

      【C++11】move和forward 、左值右值(性能优化之道)

      太长不看细节: TL;DR:

      (1) 问题: 临时变量copy开销太大

      (2) 引入: rvalue, lvalue, rvalue reference概念

      (3) 方法: rvalue reference传临时变量, move语义避免copy

      (4) 优化: forward同时能处理rvalue/lvalue reference和const reference


      下面是细节了

      两个C++的基础背景

      1. C++传值默认是copy
      2. copy开销很大

      C++11前的状况: 没法避免临时变量的copy

      基于以上背景, C++11以前是没法避免copy临时变量的, 如下面例子, 他们都要经历至少一次复制操作:

      func("some temporary string"); //初始化string, 传入函数, 可能会导致string的复制
      
      v.push_back(X()); //初始化了一个临时X, 然后被复制进了vector
      
      a = b + c; //b+c是一个临时值, 然后被赋值给了a
      
      x++; //x++操作也有临时变量的产生
      
      a = b + c + d; //c+d一个临时变量, b+(c+d)另一个临时变量

      (这些临时变量在C++11里被定义为rvalue, 右值, 因为没有对应的变量名存他们)

      (同时有对应变量名的被称为lvalue, 左值)

      案例:

      以上copy操作有没有必要呢? 有些地方可不可以省略呢? 我们来看看下面一个案例, 之后我们会用到它来推导出为什么我们需要move和forward:

      假如有一个class A, 带有一个set函数, 可以传两个参数赋值class里的成员变量:

      class A{...};
      
      void A::set(const string & var1, const string & var2){
        m_var1 = var1;  //copy
        m_var2 = var2;  //copy
      }

      下面这个写法是没法避免copy的, 因为怎么着都得把外部初始的string传进set函数, 再复制给成员变量:

      A a1;
      string var1("string1");
      string var2("string2");
      a1.set(var1, var2); // OK to copy

      但下面这个呢? 临时生成了2个string, 传进set函数里, 复制给成员变量, 然后这两个临时string再被回收. 是不是有点多余?

      A a1;
      a1.set("temporary str1","temporary str2"); //temporary, unnecessary copy

      上面复制的行为, 在底层的操作很可能是这样的:

      (1)临时变量的内容先被复制一遍

      (2)被复制的内容覆盖到成员变量指向的内存

      (3)临时变量用完了再被回收

      这里能不能优化一下呢? 临时变量反正都要被回收, 如果能直接把临时变量的内容, 和成员变量内容交换一下, 就能避免复制了? 如下:

      (1)成员变量内部的指针指向"temporary str1"所在的内存

      (2)临时变量内部的指针指向成员变量以前所指向的内存

      (3)最后临时变量指向的那块内存再被回收

      上面这个操作避免了一次copy的发生, 其实它就是所谓的move语义.

      C++11: 引入rvalue, lvalue和move

      那么这个临时变量, 在以前是解决不了了. 为了填这个坑, 蛋疼的C++委员会就说, 不如把C++搞得更复杂一些吧!

      于是就引入了rvalue和lvalue的概念, 之前说的那些临时变量就是rvalue. 上面说的避免copy的操作就是std::move

      再回到我们的例子:

      没法避免copy操作的时候, 还是要用const T&把变量传进set函数里, 现在 T&叫lvalue reference(左值引用)了, 如下:

      void set(const string & var1, const string & var2){
        m_var1 = var1;  //copy
        m_var2 = var2;  //copy
      }
      A a1;
      string var1("string1");
      string var2("string2");
      a1.set(var1, var2); // OK to copy

      传临时变量的时候, 可以传T&&, 叫rvalue reference(右值引用), 它能接收rvalue(临时变量), 之后再调用std::move就避免copy了.

      void set(string && var1, string && var2){
        //avoid unnecessary copy!
        m_var1 = std::move(var1);  
        m_var2 = std::move(var2);
      }
      A a1;
      //temporary, move! no copy!
      a1.set("temporary str1","temporary str2");

      新的问题: 避免重复

      现在终于能处理临时变量了, 但如果按上面那样写, 处理临时变量用右值引用string &&, 处理普通变量用const引用const string &...

      这代码量有点大呀? 每次都至少要写两遍(两个重载函数), overload一个新的method吗?

      回忆一下程序员的核心价值观是什么? 避免重复!

      std::forward

      perfect forward (完美转发)

      上面说的各种情况, 包括传const T &, T &&, 都可以由以下操作代替:

      template<typename T1, typename T2>
      void set(T1 && var1, T2 && var2){
        m_var1 = std::forward<T1>(var1);
        m_var2 = std::forward<T2>(var2);
      }
      
      //when var1 is an rvalue, std::forward<T1> equals to static_cast<[const] T1 &&>(var1)
      //when var1 is an lvalue, std::forward<T1> equals to static_cast<[const] T1 &>(var1)

      forward能转发下面所有的情况:

      [const] T &[&]

      也就是:

      const T &
      T &
      const T &&
      T &&

      那么forward就是上面一系列操作的集大成者.

      如果外面传来了rvalue临时变量, 它就转发rvalue并且启用move语义.

      如果外面传来了lvalue, 它就转发lvalue并且启用复制. 然后它也还能保留const.

      这样就能完美转发(perfect forwarding)所有情况了.

      那我们有了forward为什么还要用move?

      技术上来说, forward确实可以替代所有的move.

      但还有一些问题:

      首先, forward常用于template函数中, 使用的时候必须要多带一个template参数T: forward<T>, 代码略复杂;

      还有, 明确只需要move的情况而用forward, 代码意图不清晰, 其他人看着理解起来比较费劲.

      更技术上来说, 他们都可以被static_cast替代. 为什么不用static_cast呢? 也就是为了读着方便易懂.

      总结

      到这里, move和forward为什么会出现, 有什么用就彻底搞明白了. 其实也就是引入了好几个复杂概念, 来填临时变量的一个坑.

      理解C和C++中的左值和右值 

      简单定义


      lvalue(locator value)代表一个在内存中占有确定位置的对象(换句话说就是有一个地址)。
      rvalue  rvalue是不在内存中占有确定位置的表达式。

      左值:有址值

      右值:无址值 (只是计算的周期驻留在临时的寄存器中)

      基本例子

      int var;
      var = 4;

      赋值运算符要求一个lvalue作为它的左操作数,var是一个左值,因为它是一个占确定内存空间的对象。另外下面的代码是无效的:

      4 = var;        //ERROR!
      (var + 10) = 4; //ERROR!

      常量4和表达式var+1都不是lvalue(它们是rvalue)。因为都是表达式的临时结果,没有确定的内存空间(它们只是计算的周期驻留在临时的寄存器中)。因此给它们赋值没有语意-这里没有地方给它们赋值。

       

      int foo() { return 2; }
      
      int main()
      {
          foo() = 2;
          return 0;
      }

       foo返回一个临时的rvalue。尝试给它赋值,foo()=2,是一个错误;编译器期待在赋值运算符的左部分看到一个lvalue。
      不是所有的对函数调用结果赋值都是无效的。比如,C++的引用(reference)让这成为可能:

      int globalvar = 20;
      
      int& foo()
      {
          return globalvar;
      }
      
      int main()
      {
          foo() = 10;
          return 0;
      }

      这里foo返回一个引用,这是一个左值,所以它可以被赋值。C++从函数中返回左值的能力对于实现一些重载运算符时很重要的。一个普遍的例子是在类中为实现某种查找访问而重载中括号运算符 []。std::map可以这样做。

      std::map<int, float> mymap;
      mymap[10]=5.6;

      给 mymap[10] 赋值是合法的因为非const的重载运算符 std::map::operator[] 返回一个可以被赋值的引用。

       

      ‘&’ 符号在C++中扮演了另一个重要角色-它允许定义 引用类型。这被称为“左值引用”。非const左值引用不能被赋右值,因为这将要求一个无效的右值到左值的转换:

      std::string& sref = std::string(); //错误:无效的初始化,
      //用一个右值类型‘std::string’初始化非const引用类型‘std::string&’
      • 常量左值引用可以被赋右值。因为它们是常量,不能通过引用被修改,因此修改一个右值没问题。这使得C++中接受常量引用作为函数形参成为可能,这避免了一些不必要的临时对象的拷贝和构造。
      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.csdn.net/bandaoyu/article/details/107503093,作者:bandaoyu,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:C++——二叉搜索树

      下一篇:【多线程】c++11多线程编程(三)——竞争条件与互斥锁

      相关文章

      2025-05-14 10:07:38

      C++ 11新特性之auto和decltype

      在C++ 11标准中,引入了两大关键类型推导机制,即:auto关键字和decltype表达式。这两个特性不仅极大地简化了代码编写,提升了可读性,还为开发者提供了更加灵活、直观的类型声明方式。

      2025-05-14 10:07:38
      auto , 函数 , 变量 , 类型 , 表达式
      2025-05-14 10:07:38

      30天拿下Rust之所有权

      在编程语言的世界中,Rust凭借其独特的所有权机制脱颖而出,为开发者提供了一种新颖而强大的工具来防止内存错误。这一特性不仅确保了代码的安全性,还极大地提升了程序的性能。

      2025-05-14 10:07:38
      data , Rust , 内存 , 函数 , 变量 , 数据
      2025-05-14 10:03:05

      C++ 11新特性之完美转发

      在C++编程语言的演进过程中,C++ 11标准引入了一系列重大革新,其中之一便是“完美转发”机制。这一特性使得模板函数能够无损地传递任意类型的实参给其他函数或构造函数,从而极大地增强了C++在泛型编程和资源管理方面的灵活性与效率。

      2025-05-14 10:03:05
      amp , 函数 , 右值 , 引用 , 模板 , 类型
      2025-05-14 10:03:05

      C++ 11新特性之右值引用

      C++ 11中引入了一项关键特性——右值引用,极大地增强了C++在资源管理、性能优化和表达力方面的能力。通过理解并合理运用右值引用,我们可以编写出更高效、更简洁且不易出错的代码。

      2025-05-14 10:03:05
      右值 , 对象 , 常量 , 引用 , 构造函数 , 绑定
      2025-05-13 09:53:23

      Java静态变量在静态方法内部无法改变值

      在Java中,静态变量(也称为类变量)属于类本身,而不是类的任何特定实例。它们可以在没有创建类的实例的情况下访问和修改。如果我们发现在静态方法内部无法改变静态变量的值,这通常是因为我们的代码中有一些逻辑错误或误解。

      2025-05-13 09:53:23
      Java , 变量 , 实例 , 类名 , 访问 , 静态 , 静态方法
      2025-05-13 09:53:13

      计算机萌新的成长历程18——指针

      计算机要存储数据的话有以下几种途径,按访问速度由快到慢来排列分别是:寄存器>高速缓存>内存>硬盘。它们的存储空间大小是依次增大的,寄存器的存储空间大小最小,硬盘存储空间大小最大。

      2025-05-13 09:53:13
      内存 , 变量 , 地址 , 寄存器 , 指针
      2025-05-13 09:49:27

      全局变量_文件体系

      全局变量_文件体系

      2025-05-13 09:49:27
      bash , bashrc , profile , 变量 , 实践 , 文件
      2025-05-13 09:49:27

      变量基础_变量定义

      变量基础_变量定义

      2025-05-13 09:49:27
      变量 , 定义 , 示例 , 移除 , 解析 , 语法
      2025-05-13 09:49:27

      变量基础_变量场景

      变量基础_变量场景

      2025-05-13 09:49:27
      变量 , 场景 , 存储 , 学习 , 数据 , 编程语言
      2025-05-12 08:40:18

      Linux+Docer 容器化部署之 Shell 语法入门篇 【Shell变量】

      Linux+Docer 容器化部署之 Shell 语法入门篇 【Shell变量】

      2025-05-12 08:40:18
      Linux , 变量
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5248957

      查看更多

      最新文章

      C++ 11新特性之auto和decltype

      2025-05-14 10:07:38

      C++ 11新特性之完美转发

      2025-05-14 10:03:05

      C++ 11新特性之右值引用

      2025-05-14 10:03:05

      Java静态变量在静态方法内部无法改变值

      2025-05-13 09:53:23

      变量基础_变量场景

      2025-05-13 09:49:27

      C语言的32个关键字

      2025-05-07 09:08:23

      查看更多

      热门文章

      指针(*)、取地址(&)、解引用(*)与引用(&)

      2023-04-10 08:54:19

      Go语言开发(3)变量

      2023-05-08 10:00:08

      C++系列三:变量、常量

      2023-06-13 08:29:57

      C#编程-14:自定义环境变量

      2023-05-23 09:44:38

      1.3 常量与变量

      2023-06-20 09:18:01

      PHP基础——变量传值

      2023-07-24 09:44:23

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      arduino程序-变量(基础知识)

      Java类的初级认识

      Java变量和数据类型

      【C深度解剖】const关键字

      shell编程规范与变量

      Java之变量的学习

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号