当前位置: 首页 > news >正文

【C++11】左值引用、右值引用和移动语义

🎏左值和左值引用

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址,一般可以对它赋值,左值可以出现在赋值符号的左边,右值不能出现在赋值符号的左边.定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址,因此还是左值.

左值引用就是给左值的引用,给左值取别名.如:

代码语言:javascript

AI代码解释

//左值引用 int a = 0; int& r1 = a; //给a取别名为r1
🎏右值和右值引用

右值是一个表示数据的表达式,如:字面常量,表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,右值不能取地址.

右值引用就是对右值的引用,给右值取别名.如:

代码语言:javascript

AI代码解释

//右值引用 int&& r5 = 10; //给10取别名为r5 double x = 1.1, y = 2.2; double&& r6 = x + y; //给表达式x+y取别名为r6

📌左值引用和右值引用比较

🎏左值引用
  1. 左值引用只能引用左值,不能引用右值
  2. 但是const左值引用既可以引用左值,也可以引用右值

代码语言:javascript

AI代码解释

/左值引用引用右值 double x = 2.2; double y = 3.3; const int& r2 = 10; const double& r3 = x + y;//这里x和y都是左值,但是x+y表达式返回的结果5是一个临时变量是右值
🎏右值引用
  1. 右值引用只能引用右值,不能引用左值.
  2. 但是特殊情况下右值引用可以引用move以后的左值.

代码语言:javascript

AI代码解释

//右值引用引用左值 int a = 10; int&& r7 = move(a);

也就是说,正常情况下左值只能引用左值, 右值只能引用右值, 但是const左值可以引用右值,右值可以引用move后的左值。

📌左值引用和右值引用使用场景和意义

🎏左值引用使用场景和意义

左值引用使用场景:

做参数

代码语言:javascript

AI代码解释

void swap(int& a,int&b) //左值引用可以直接修改原对象,减少参数传递时的拷贝 { int tmp = a; a = b; b = tmp; } int main() { int x = 2; int y = 3; swap(x,y); return 0; }

做返回值

代码语言:javascript

AI代码解释

//左值引用可以直接修改返回值,同时减少了函数传值返回的拷贝 int& get(size_t pos) { return data[pos]; }

左值引用意义:减少拷贝,并可以直接修改原对象

左值引用的缺点:但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。例如:

函数中可以看到,这里只能使用传值返回,传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)。

🎏右值引用使用场景和意义

通过上面我们对左值引用使用场景和意义的分析,我们得知了左值引用的短板。因此C++的大佬们就引入了右值引用和移动语义来解决这个问题:移动语义包括移动构造和移动赋值,我们先来看移动构造:

移动构造本质是将参数右值的资源窃取过来,占为已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己:

而移动赋值也是将赋值运算符右边的右值资源窃取过来,占为己有,也就不用再做深拷贝了:

基于上面的概念,实现的string类移动构造和移动赋值函数如下:

代码语言:javascript

AI代码解释

//移动构造 string(string&& s) :_str(nullptr) , _size(0) , _capacity(0) { swap(s); } //移动赋值 string& operator=(string&& s) { swap(s); return *this; }
🎏右值引用引用左值及其使用场景

有些场景下,我们可能需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。

代码语言:javascript

AI代码解释

int main() { string s1("hello world"); // 这里s1是左值,调用的是拷贝构造 string s2(s1); // 这里我们把s1 move处理以后, 会被当成右值,调用移动构造 // 但是这里要注意,一般是不要这样用的,因为我们会发现s1的 // 资源被转移给了s3,s1被置空了。 string s3(std::move(s1)); return 0; }

📌完美转发

完美转发(Perfect Forwarding)是 C++11 引入的核心特性之一,用于在泛型编程中精确传递参数的左值/右值属性,避免不必要的拷贝或类型损失。它结合了右值引用万能引用(Universal Reference)std::forward实现。

🎏为什么需要完美转发?

假设有一个泛型函数wrapper,需要将参数转发给另一个函数target

代码语言:javascript

AI代码解释

template<typename T> void wrapper(T arg) { target(arg); // 直接传递参数 }

问题:

  1. 值类别丢失:无论arg是左值还是右值,target(arg)接收的始终是左值(因为右值引用本身是左值, 如果右值引用本身是右值那么就没法移动语义了)所以左值引用和右值引用传递到下层都变成了左值引用。
  2. 拷贝开销:若arg是临时对象(右值),无法触发移动语义,可能导致深拷贝。

右值引用默认是左值,我们才能基于此实现移动语义:

但是如果不支持完美转发的话,右值引用无法保持右值属性,那么我们遇到嵌套容器深拷贝的情况就没法用移动语义:

🎏如何实现完美转发?

1. 万能引用(Universal Reference)

  • 语法:模板参数中使用T&&,且T需要被推导(如函数模板或auto)。
  • 特性:可以绑定到左值或右值,保留参数的原始类型信息。
  • 如果实参是左值,他就是左值引用(引用折叠)
  • 如果实参是右值,他就是右值引用

代码语言:javascript

AI代码解释

template<typename T> void wrapper(T&& arg) { // arg 是万能引用 // 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。 // 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力, // 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值, // 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发 }

2.std::forward<T>

  • 作用:根据T的原始类型(左值或右值),将参数有条件地转换回原始类型
  • 本质:若T是左值引用,返回左值;否则返回右值引用(触发移动语义)。
🎏完整示例

代码语言:javascript

AI代码解释

#include <iostream> #include <utility> // std::forward // 目标函数 void target(int& x) { std::cout << "左值: " << x << std::endl; } void target(int&& x) { std::cout << "右值: " << x << std::endl; } // 完美转发的包装函数 template<typename T> void wrapper(T&& arg) { target(std::forward<T>(arg)); // 关键:保留参数的原始类型 } int main() { int a = 10; wrapper(a); // 传递左值 → 调用 target(int&) wrapper(20); // 传递右值 → 调用 target(int&&) wrapper(std::move(a)); // 显式转为右值 → 调用 target(int&&) return 0; }

结语

希望这篇关于 C++11之左值引用,右值引用和移动语义 的博客能对大家有所帮助,欢迎大佬们留言或私信与我交流.学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

http://www.jsqmd.com/news/733744/

相关文章:

  • 喀什、和田租车怎么选?2026多品牌实测对比:全场景适配,政企/个人用车首选推荐 - GrowthUME
  • 游戏升级记 2 - ace-
  • 智慧园区——解读智园新环境下智慧化工园区建设的标准规范与关注重点
  • 零代码实现PPTX转HTML:浏览器端一键转换完整指南
  • C++20 内存模型与并发的变更
  • 总之就是一大堆莫队——
  • 2026年选太阳能路灯厂家,这三点关键指标别忽视 - 速递信息
  • VisualCppRedist AIO:终极解决方案!一键修复Windows所有VC++运行库问题
  • C++异常处理完全指南:从原理到实战
  • A001.金戈企业网站搭建
  • 2026年,邯郸GEO运营解决方案公司哪家强?答案即将揭晓! - 速递信息
  • 别再手动填Excel了!用阿里EasyExcel实现省/市/区三级联动下拉,附完整Java代码
  • 多线程——面试中常考的内容(11)
  • 3步彻底解决Visual C++运行库问题:VisualCppRedist AIO完全指南
  • Lean 4定理验证:方法论与工程实践
  • PHP V6 单商户常见问题——升级提示mkdir()处理方案
  • 终极二维码修复指南:QRazyBox让你的失效二维码重获新生
  • 2026 佐米曲普坦临床选药与评测深度指南:偏头痛患者的高性价比优选 - GrowthUME
  • MARS算法原理与Python实现:非线性回归实战指南
  • 【c++】异常处理
  • MCP 2026医疗数据安全防护“红蓝对抗”实战手册(内部流出版):覆盖CT/MRI/病理全模态攻击链与17个防御卡点
  • 01 用栈实现队列
  • 大气层系统完整指南:Switch自定义固件的终极解决方案
  • Moonlight-Switch:Nintendo Switch游戏串流技术方案与多平台兼容架构
  • taotoken 平台 python 调用 openai 兼容 api 的完整入门指南
  • 借助模型广场与官方折扣为新项目选择高性价比模型
  • 解锁旧Mac新生命:OpenCore Legacy Patcher完全指南
  • C++中string常用方法总结
  • 2026年扬州工厂短视频代运营案例分析 - 速递信息
  • 2026企业AI陪跑推荐:全程陪伴,落地见效 8 - 速递信息